Android Game Framework using Properties
Introduction
See the preceding page for notes on language features and a version of the code with getter and setter methods for private variables (fields). Here we use public properties and private fields. The read and write keywords map each property to the private field containing its value. You can instead map a property to an accessor method e.g. with the code write setWidth instead of write fWidth for the Width property of the AlienObject class below. You might then include in the setWidth method some validation to ensure acceptable values. The code that uses the property would still work. (Instead of using properties you can handle public fields directly. Although such code is more difficult to modify and is considered to be bad practice, it saves time for a beginner).
property Bounds : Rect read new Rect(X, Y, X + Width, Y + Height);
The absence of a write specifier makes Bounds a read-only property. Note that it is conventional to begin the property identifier with a capital letter and that otherwise the compiler will provide a warning.
The code of MainActivity.pas
namespace org.me.game_framework2; interface uses android.app, android.os, android.util, android.view, android.widget, android.content.*, android.graphics.*, java.util.concurrent; type MainActivity = public class(Activity) private const DX = 30; DY = 30; var dp : DrawingPanel; btnUp, btnDown, btnLeft, btnRight, btnRestart : Button; myAlien : AlienObject; class var fCollided : Boolean := false; protected method onCreate(savedInstanceState : Bundle); override; method ButtonOnClick(v : View); public class property Collided : Boolean read fCollided write fCollided; end; DrawingPanel = class(SurfaceView, SurfaceHolder.Callback) private fAlien, fFinalAlien : AlienObject; alien_id : Integer; imgAlien : Drawable; fBMP : Bitmap; rectObstacle : Rect := new Rect(150, 250, 250, 260); paint : Paint; fThread : PanelThread; public property PThread : PanelThread read fThread write fThread; property Alien : AlienObject read fAlien write fAlien; property FinalAlien : AlienObject read fFinalAlien write fFinalAlien; property BMP : Bitmap read fBMP write fBMP; constructor(context : Context); method onDraw(canvas : Canvas); override; method surfaceChanged(arg0 : SurfaceHolder; arg1, arg2, arg3 : Integer); method surfaceCreated(holder : SurfaceHolder); method surfaceDestroyed(holder : SurfaceHolder); end; AlienObject = class(Object) private fX, fY, fWidth, fHeight : Integer; fCrashed : Boolean := false; public property X : Integer read fX write fX; property Y : Integer read fY write fY; property Width : Integer read fWidth write fWidth; property Height : Integer read fHeight write fHeight; property Bounds : Rect read new Rect(X, Y, X + Width, Y + Height); constructor(newX, newY, newWidth, newHeight : Integer); end; PanelThread = class(Thread) private fSurfaceHolder : SurfaceHolder; fPanel : DrawingPanel; fRun : Boolean := false; public property S_Holder : SurfaceHolder read fSurfaceHolder write fSurfaceHolder; property D_Panel : DrawingPanel read fPanel write fPanel; property IsRunning : Boolean read fRun write fRun; constructor (surfaceHolder : SurfaceHolder; panel : DrawingPanel); method setRunning(running : Boolean); //Allow us to stop the thread method run; override; end; implementation method MainActivity.ButtonOnClick(v : View); var tempAlien : AlienObject; begin tempAlien := dp.Alien; if v.equals(btnUp) then tempAlien.Y := tempAlien.Y - DY else if v.equals(btnDown) then tempAlien.Y := tempAlien.Y + DY else if v.equals(btnLeft) then tempAlien.X := tempAlien.X - DX else if v.equals(btnRight) then tempAlien.X := tempAlien.X + DX else if v.equals(btnRestart) and Collided then begin tempAlien.X := 10; tempAlien.Y := 20; setCollided(false); end; dp.Alien := tempAlien; end; method MainActivity.onCreate(savedInstanceState : Bundle); var mainLayout, buttonLayout : LinearLayout; begin inherited; mainLayout := new LinearLayout(Self); //Make a linear layout that fills the screen and centres child views horizontally & vertically mainLayout.Orientation := LinearLayout.VERTICAL; mainLayout.LayoutParams := new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); mainLayout.Gravity := Gravity.CENTER_VERTICAL or Gravity.CENTER_HORIZONTAL; //Make a linear layout for a row of four buttons buttonLayout := new LinearLayout(Self); buttonLayout.Orientation := LinearLayout.HORIZONTAL; buttonLayout.LayoutParams := new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); buttonLayout.Gravity := Gravity.CENTER_VERTICAL or Gravity.FILL_HORIZONTAL; //Create 4 buttons btnUp := new Button(Self); btnUp.Text := 'Up'; btnUp.Width := 100; btnDown := new Button(Self); btnDown.Text := 'Down'; btnDown.Width := 100; btnLeft := new Button(Self); btnLeft.Text := 'Left'; btnLeft.Width := 100; btnRight := new Button(Self); btnRight.Text := 'Right'; btnRight.Width := 100; btnRestart := new Button(self); btnRestart.Text := 'Restart'; btnRestart.Width := 100; //Add buttons to their layout buttonLayout.addView(btnUp); buttonLayout.addView(btnDown); buttonLayout.addView(btnLeft); buttonLayout.addView(btnRight); buttonLayout.addView(btnRestart); mainLayout.addView(buttonLayout); //Adds button layout to main layout dp := new DrawingPanel(self); //Creates drawing panel mainLayout.addView(dp); //Adds drawing panel to main layout ContentView := mainLayout; //Set listeners btnUp.setOnClickListener(@ButtonOnClick); btnDown.setOnClickListener(@ButtonOnClick); btnLeft.setOnClickListener(@ButtonOnClick); btnRight.setOnClickListener(@ButtonOnClick); btnRestart.setOnClickListener(@ButtonOnClick); end; constructor DrawingPanel(context : Context); begin inherited; getHolder.addCallback(self); paint := new Paint; paint.setStyle(paint.Style.FILL); alien_id := Resources.getIdentifier('alien', "drawable", context.PackageName); imgAlien := Resources.getDrawable(alien_id); BMP := BitmapDrawable(imgAlien).getBitmap; Alien := new AlienObject(10, 20, BMP.Width, BMP.Height); FinalAlien := new AlienObject(0, 0, BMP.Width, BMP.Height); end; method DrawingPanel.onDraw(canvas : Canvas); begin paint.setColor($ff000060); canvas.drawPaint(paint); // makes the entire canvas dark blue paint.setColor(Color.YELLOW); canvas.drawRect(rectObstacle, paint); if not MainActivity.Collided then begin canvas.drawBitmap(BMP, Alien.X, Alien.Y, paint); if Rect.intersects(Alien.Bounds, rectObstacle) then begin FinalAlien.X := Alien.X; FinalAlien.Y := Alien.Y; MainActivity.Collided := true; end; end else begin canvas.drawBitmap(BMP, FinalAlien.X, FinalAlien.Y, paint); paint.setColor(Color.MAGENTA); paint.TextSize := 48; canvas.drawText('Game Over', 100, 250, paint); end; end; method DrawingPanel.surfaceChanged(arg0 : SurfaceHolder; arg1, arg2, arg3 : Integer); begin end; method DrawingPanel.surfaceCreated(holder : SurfaceHolder); begin setWillNotDraw(false); //Allows us to use postInvalidate to call onDraw PThread := new PanelThread(getHolder, self); //Start the thread that PThread.setRunning(true); //will make calls to PThread.start; //onDraw end; method DrawingPanel.surfaceDestroyed(holder : SurfaceHolder); begin try PThread.setRunning(false); //Tells thread to stop PThread.join; //Removes thread from mem. except on e : InterruptedException do begin end; end; end; constructor AlienObject(newX, newY, newWidth, newHeight : Integer); begin fX := newX; fY := newY; fWidth := newWidth; fHeight := newHeight; end; constructor PanelThread(surfaceHolder : SurfaceHolder; panel : DrawingPanel); begin S_Holder := surfaceHolder; D_Panel := panel; end; method PanelThread.setRunning(running : Boolean); //Allow us to stop the thread begin IsRunning := running; end; method PanelThread.run; var c : Canvas; tempAlien : AlienObject; begin while IsRunning do //When setRunning(false) occurs, IsRunning is begin //set to false and loop ends, stopping thread c := nil; try c := S_Holder.lockCanvas(nil); locking S_Holder do begin tempAlien := D_Panel.Alien; tempAlien.X := tempAlien.X + 1; if (tempAlien.X + tempAlien.Width) > D_Panel.Right then tempAlien.X := D_Panel.Left; tempAlien.Y := tempAlien.Y + 1; if (tempAlien.Y + tempAlien.Height) > S_Holder.SurfaceFrame.bottom then tempAlien.Y := S_Holder.SurfaceFrame.top; D_Panel.Alien := tempAlien; D_Panel.postInvalidate; sleep(5); end; finally if not (c = nil) then S_Holder.unlockCanvasAndPost(c); end; end; end; end.
Strings File
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Game Framework</string> </resources>