Android Tutorial [B4X] [BitmapCreator] Cross platform Falling Sand game

Erel

Administrator
Staff member
Licensed User


The main challenge here is the performance. All particles are updated if needed and redrawn every cycle.

The Particles array stores the state of all "game pixels". A value of 0 means that the pixel is empty. Other values mean that there is a particle on that pixel.
Sand particles move down or diagonally if they can.

The RowsState array tracks the rows that are considered dirty. Non-dirty rows are skipped.

Single dimension arrays of bytes can be accessed in an optimized way in B4i with Bit.FastArrayGet / Set methods (https://www.b4x.com/android/forum/threads/92951/#content). I used these methods in some of the cases.

All the important code is implemented in the Game class, which is of course shared between the three projects. No sprites are used in this game. The drawings are done in the MoveParticles sub.
As in the Tetris game the layout is managed with the designer script.

You need to change the GameUtils module that is included in the zip files with the one attached separately.

Dependencies:

- Recent version of XUI.
- BitmapCreator v3.6 (B4J, B4i)

The three projects are attached. I've also uploaded an executable jar. Download and double click.
www.b4x.com/b4j/files/games/FallingSand_ExecutableJar.jar
 

Attachments

Last edited:

mark35at

Well-Known Member
Licensed User
Obviously Erel has 36 hours in his day! Otherwise he wouldn't have time for such great things between writing updates and answering questions. Great post! :D
 

Erel

Administrator
Staff member
Licensed User
Projects in the first post were updated with a fix to a bug that caused the game to crash.

The cause of this bug might be a bit interesting as it is a pattern that can be used in many cases.

When the particles move we need to check the status of the nearby pixels. This causes a problem with the pixels near the edge. We can add checks to assure that we don't try to access out of bounds pixels but it can be annoying to always check it when 99.5% of the pixels are known to be inside.
So instead we are creating ROCK borders when the game starts:
B4X:
'create the borders
For y = 0 To Height - 1
   Particles((y + 1) * Width - 1) = ROCK
   Particles(y * Width + 1) = ROCK
Next
For x = 0 To Width - 1
   Particles ((Height - 1) * Width + x) = ROCK
   Particles (x) = ROCK
Next
This way there are less edge cases to deal with.

So where was the bug?

With the Eraser feature. It didn't check for the edges and could have erased the borders themselves. This feature is the only feature that overwrites other types of particles.
This caused the sand particles to fall outside of the BitmapCreator and even outside of the phone in some cases. The fix is to restrict the erasing position. It was added to AddParticles sub.
 

koaunglay

Member
Licensed User
Please! Why can't I test this project?
Help

B4X:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.DynamicBuilder.build(DynamicBuilder.java:21)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayoutHelper(LayoutBuilder.java:349)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayoutHelper(LayoutBuilder.java:454)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:148)
    at anywheresoftware.b4a.objects.PanelWrapper.LoadLayout(PanelWrapper.java:134)
    at anywheresoftware.b4a.objects.B4XViewWrapper.LoadLayout(B4XViewWrapper.java:247)
    at b4a.example.game._initialize(game.java:298)
    at b4a.example.main._activity_create(main.java:342)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
    at b4a.example.main.afterFirstLayout(main.java:104)
    at b4a.example.main.access$000(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:82)
    at android.os.Handler.handleCallback(Handler.java:754)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:163)
    at android.app.ActivityThread.main(ActivityThread.java:6342)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:880)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
Caused by: java.lang.RuntimeException: Font asset not found b4x_fontawesome.otf
    at android.graphics.Typeface.createFromAsset(Typeface.java:206)
    at anywheresoftware.b4a.objects.TextViewWrapper.getTypeface(TextViewWrapper.java:155)
    at anywheresoftware.b4a.objects.TextViewWrapper.build(TextViewWrapper.java:183)
    at anywheresoftware.b4a.objects.LabelWrapper.build(LabelWrapper.java:36)
    ... 21 more
game_initialize (java line: 298)
java.lang.RuntimeException: java.lang.RuntimeException: Object should first be initialized (Label).
Did you forget to call Activity.LoadLayout?
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:170)
    at anywheresoftware.b4a.objects.PanelWrapper.LoadLayout(PanelWrapper.java:134)
    at anywheresoftware.b4a.objects.B4XViewWrapper.LoadLayout(B4XViewWrapper.java:247)
    at b4a.example.game._initialize(game.java:298)
    at b4a.example.main._activity_create(main.java:342)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:191)
    at b4a.example.main.afterFirstLayout(main.java:104)
    at b4a.example.main.access$000(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:82)
    at android.os.Handler.handleCallback(Handler.java:754)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:163)
    at android.app.ActivityThread.main(ActivityThread.java:6342)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:880)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
Caused by: java.lang.RuntimeException: Object should first be initialized (Label).
Did you forget to call Activity.LoadLayout?
    at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
    at anywheresoftware.b4a.objects.ViewWrapper.innerInitialize(ViewWrapper.java:73)
    at anywheresoftware.b4a.objects.LabelWrapper.innerInitialize(LabelWrapper.java:28)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayoutHelper(LayoutBuilder.java:435)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayoutHelper(LayoutBuilder.java:454)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:148)
    ... 16 more
 
Top