# teaching machines

## CS 436 Lecture 20 – The Game Loop (on Android)

October 31, 2014 by . Filed under cs436, fall 2014, lectures.

### Agenda

• what ?s
• custom animation on the UI thread
• SurfaceView
• the game loop

### Code

#### Particle.java

package org.twodee.rain;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

import java.util.Random;

public class Particle {
private static Random g = new Random();

private Paint paint;
private float x;
private float y;
private float velocityX;
private float velocityY;

public Particle(int width, int height) {
x = g.nextInt(2 * width);
y = -g.nextInt(height);
velocityX = g.nextFloat() * -100.0f - 70.0f;
velocityY = g.nextFloat() * 100.0f + 250.0f;

paint = new Paint();
paint.setColor(Color.GRAY);
paint.setStrokeWidth(3.0f);
}

public void update(float elapsedSeconds) {
x += velocityX * elapsedSeconds;
y += velocityY * elapsedSeconds;
}

public void draw(Canvas canvas) {
canvas.drawLine(x, y, x - 15, y + 40, paint);
}

public boolean isDead(int width, int height) {
return y > height || x < 0;
}
}

#### ParticleSystem.java

package org.twodee.rain;

import android.graphics.Canvas;

public class ParticleSystem {
private Particle[] particles;

public ParticleSystem(int width, int height) {
particles = new Particle[500];
for (int i = 0; i < particles.length; ++i) {
particles[i] = new Particle(width, height);
}
}

public void update(Canvas canvas, float elapsedSeconds) {
for (int i = 0; i < particles.length; ++i) {
particles[i] = new Particle(canvas.getWidth(), canvas.getHeight());
}
particles[i].update(elapsedSeconds);
}
}

public void draw(Canvas canvas) {
for (Particle particle : particles) {
particle.draw(canvas);
}
}
}

#### MainActivity.java

package org.twodee.rain;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;

public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

#### AnimationView.java

package org.twodee.rain;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class AnimationView extends SurfaceView implements Callback {

private SurfaceHolder holder;
private int width;
private int height;

public AnimationView(Context context,
AttributeSet attrs) {
super(context, attrs);
holder = getHolder();

//    system = new ParticleSystem()
}

@Override
public void surfaceCreated(SurfaceHolder holder) {

}

@Override
public void surfaceChanged(SurfaceHolder holder,
int format,
int width,
int height) {
this.width = width;
this.height = height;
dt.start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
dt.blockTillJoined();
}

private ParticleSystem system;
private long lastFrameAt;
private boolean isRequestedToStop;

lastFrameAt = SystemClock.uptimeMillis();
system = new ParticleSystem(width, height);
isRequestedToStop = false;
}

protected void draw(Canvas canvas) {
long currentFrameAt = SystemClock.uptimeMillis();
float elapsedSeconds = (currentFrameAt - lastFrameAt) / 1000.0f;
system.update(canvas, elapsedSeconds);
canvas.drawColor(Color.BLACK);
system.draw(canvas);
lastFrameAt = currentFrameAt;
}

@Override
public void run() {
super.run();
while (!isRequestedToStop) {
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
draw(canvas);
holder.unlockCanvasAndPost(canvas);
}
Log.d("FOO", "Any junk you want");
}
}

public void blockTillJoined() {
isRequestedToStop = true;
boolean isJoined = false;

while (!isJoined) {
try {
join();
isJoined = true;
} catch (InterruptedException e) {
}
}
}
}
}

#### activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<view
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="org.twodee.rain.AnimationView"
android:id="@+id/view"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>