CS 436 Lecture 20 – The Game Loop (on Android)
October 31, 2014 by Chris Johnson. 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) {
if (particles[i].isDead(canvas.getWidth(), canvas.getHeight())) {
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;
private DrawingThread dt;
public AnimationView(Context context,
AttributeSet attrs) {
super(context, attrs);
holder = getHolder();
holder.addCallback(this);
// 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 = new DrawingThread();
dt.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
dt.blockTillJoined();
}
private class DrawingThread extends Thread {
private ParticleSystem system;
private long lastFrameAt;
private boolean isRequestedToStop;
private DrawingThread() {
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>