I finished my GrowWithGoogle / Udacity Android Development class early, so I began fishing around for other projects to
try out. A few months ago, I had implemented a full-featured, BlackJack game in Unity / C#. It was a UI-driven game and I felt
comfortable that I had learned enough XML and Java to port the game to Android.
The original Unity / C# code is here https://github.com/sdocy/blackjack,
project website https://sdocy.github.io/blackjack/
Highlights:
- Most of code actually ported quite easily.
- Even swapping the Unity button, text and image objects for XML button, text and image views was pretty straightforward.
- Getting proper game delays was trickier than in Unity, but it gave me a chance to play with Java multi-threading.
- I did not port support for splitting cards. Being able to display multiple player hands on a small mobile screen will require
a new card layout approach.
- The absence of pass-by-reference in Java resulted in my creating a player class to be able to have Java methods that work on both
the player's hand and the dealer's hand. This actually turned out extremely well. Not only could I re-use the code I wanted for
both the player and the dealer, but it made the blackjack code feel much more streamlined. Go go OOD!
Gory details:
- I didn't port the simulation code, but it was kept separate enough, that keeping it out of the port was straight-forward
- As expected, implementing synchronous delays at specific points in the game, for example, to slow down card dealing so that the
user can follow the action, was tricky. In Unity, I had to learn how to mix asynchronous coroutines with synchronous, event-driven
code. Java presented a number of approaches to accomplishing the same thing, but none which seemed to allow multiple, successive,
synchronous delays. The approaches I investigated are:
- Thread.Sleep() and SystemClock.Sleep(), do exactly what I want, but they can't be used on the main UI thread, or
updates to the screen will not take place until after the sleep, even if they are initiated before the sleep.
- Java includes handlers and runnables for triggering the execution of code after a certain delay. But as far as I can tell,
there is no way to make the delays synchronous with respect to the thread that spawned them, so in order to get multiple,
delayed events, you would need to implement a complex, unmaintainable chain of runnables.
- So I learned how to create a new thread in Java. I figured that would allow me to use Thread.Sleep() to get the delays
I wanted. And I was correct, EXCEPT...you can only update UI elements (buttons, TextViews, ImageViews, etc.) from the main
UI thread. Ahhhhhh....seemed like every course I investigated kept running into one gotcha or another.
- I read that Looper threads may be able to update screen elements, but I couldn't find an explanation of them that was clear.
But then I saw runOnUiThread() which sounded like it would execute the specified runnable on the main UI thread, which
could then update screen elements. I tried creating a new thread, using Thread.Sleep() prior to an action that I wanted to
delay, and then using runOnUiThread() to give the task to the UI thread if it was something that updated the screen. It all
worked pretty well, and didn't turn out quite as cumbersome as it sounds, though it has the unfortunate side effect of
requiring delays after calling runOnUiThread(). I wouldn't recommend this design for anything at all complex, for for my
simple blackjack code, it was manageable,
- At some point, I will go back and try to figure out Looper threads
- Check out the code in this gist....
https://gist.github.com/sdocy/f195beeb08a29827417adb324e21c83a
- In order to make room next to the dealer's cards for the dealer card total, I had to fudge the dealer cards a little. Technically
we need to support displaying 12 cards for both player and dealer since you can get 12 cards without busting. But the odds of
that happening are extremely small, so I kept the 12th card image slot for the dealer, but moved it to where I want
the dealer card total to be displayed (to the right of the dealer's first two cards, and changed it's width to 0. So the dealer
can be dealt 12 cards, it's just that the twelfth card will not be displayed. I can live with that for now. I will likely
investigate using a RelativeLayout for the card images and overlapping them slightly to conserve real estate.