source: advance-wars/src/com/example/advancewars/GameView.java@ 5d9e7bb

Last change on this file since 5d9e7bb was 5d9e7bb, checked in by dportnoy <devnull@…>, 14 years ago

Added lots of code from the android blackjack project.

  • Property mode set to 100644
File size: 15.3 KB
Line 
1package com.example.advancewars;
2
3import java.util.*;
4
5import android.content.Context;
6import android.graphics.*;
7import android.os.*;
8import android.view.*;
9import android.util.AttributeSet;
10import android.util.Log;
11import android.widget.TextView;
12
13/**
14 * View that draws, takes keystrokes, etc. for a simple LunarLander game.
15 *
16 * Has a mode which RUNNING, PAUSED, etc. Has a x, y, dx, dy, ... capturing the
17 * current ship physics. All x/y etc. are measured with (0,0) at the lower left.
18 * updatePhysics() advances the physics based on realtime. draw() renders the
19 * ship, and does an invalidate() to prompt another draw() as soon as possible
20 * by the system.
21 */
22class GameView extends SurfaceView implements SurfaceHolder.Callback {
23
24 class DrawingThread extends Thread {
25 public AppState mAppState;
26 public GameState mGameState;
27
28 /*
29 * UI constants (i.e. the speed & fuel bars)
30 */
31 public static final int UI_BAR = 100; // width of the bar(s)
32 public static final int UI_BAR_HEIGHT = 10; // height of the bar(s)
33
34 /*
35 * Member (state) fields
36 */
37
38 private int mCanvasHeight = 1;
39 private int mCanvasWidth = 1;
40 public int mPlayerWins = 0, mPlayerLosses = 0;
41
42 /** Message handler used by thread to interact with TextView */
43 private Handler mHandler;
44
45 /** Used to figure out elapsed time between frames */
46 private long mLastTime;
47
48 /** Paint to draw the lines on screen. */
49 private Paint mLinePaint, mTextPaint, mButtonPaint;
50
51 /** Indicate whether the surface has been created & is ready to draw */
52 private boolean mRun = false;
53
54 /** Handle to the surface manager object we interact with */
55 private SurfaceHolder mSurfaceHolder;
56
57 public DrawingThread(SurfaceHolder surfaceHolder, Context context,
58 Handler handler) {
59 // get handles to some important objects
60 mSurfaceHolder = surfaceHolder;
61 mHandler = handler;
62 mContext = context;
63
64 // Initialize paints for speedometer
65 mLinePaint = new Paint();
66 mLinePaint.setAntiAlias(true);
67 mLinePaint.setARGB(255, 0, 255, 0);
68
69 mTextPaint = new Paint();
70 mTextPaint.setAntiAlias(true);
71 mTextPaint.setARGB(255, 255, 255, 255);
72 mTextPaint.setTextSize(12);
73
74 mButtonPaint = new Paint();
75 mButtonPaint.setAntiAlias(true);
76 mButtonPaint.setARGB(255, 0, 0, 0);
77 mButtonPaint.setTextSize(20);
78 mButtonPaint.setTextAlign(Paint.Align.CENTER);
79
80 mGameState = GameState.YOUR_TURN;
81 }
82
83 /**
84 * Starts the game, setting parameters for the current difficulty.
85 */
86 // I don't think this gets called now. maybe we should call it in the thread constructor
87 public void doStart() {
88 synchronized (mSurfaceHolder) {
89 mLastTime = System.currentTimeMillis() + 100;
90 setState(AppState.RUNNING);
91 Log.i("Blackjack", "Player's turn starting now");
92 mGameState = GameState.YOUR_TURN;
93 }
94 }
95
96 /**
97 * Pauses the physics update & animation.
98 */
99 public void pause() {
100 synchronized (mSurfaceHolder) {
101 if (mAppState == AppState.RUNNING) setState(AppState.PAUSE);
102 }
103 }
104
105 @Override
106 public void run() {
107 while (mRun) {
108 Canvas c = null;
109 try {
110 c = mSurfaceHolder.lockCanvas(null);
111 synchronized(mSurfaceHolder) {
112 if(mAppState == AppState.RUNNING)
113 updatePhysics();
114 doDraw(c);
115 }
116 } finally {
117 // do this in a finally so that if an exception is thrown
118 // during the above, we don't leave the Surface in an
119 // inconsistent state
120 if (c != null) {
121 mSurfaceHolder.unlockCanvasAndPost(c);
122 }
123 }
124 }
125 }
126
127 /**
128 * Used to signal the thread whether it should be running or not.
129 * Passing true allows the thread to run; passing false will shut it
130 * down if it's already running. Calling start() after this was most
131 * recently called with false will result in an immediate shutdown.
132 *
133 * @param b true to run, false to shut down
134 */
135 public void setRunning(boolean b) {
136 mRun = b;
137 }
138
139 /**
140 * Sets the game mode. That is, whether we are running, paused, in the
141 * failure state, in the victory state, etc.
142 *
143 * @see #setState(int, CharSequence)
144 * @param mode one of the STATE_* constants
145 */
146 public void setState(AppState state) {
147 synchronized (mSurfaceHolder) {
148 setState(state, null);
149 }
150 }
151
152 public void setGameState(GameState state) {
153 synchronized (mSurfaceHolder) {
154 mGameState = state;
155 }
156 }
157
158 /**
159 * Sets the game mode. That is, whether we are running, paused, in the
160 * failure state, in the victory state, etc.
161 *
162 * @param mode one of the STATE_* constants
163 * @param message string to add to screen or null
164 */
165 public void setState(AppState mode, CharSequence message) {
166 /*
167 * This method optionally can cause a text message to be displayed
168 * to the user when the mode changes. Since the View that actually
169 * renders that text is part of the main View hierarchy and not
170 * owned by this thread, we can't touch the state of that View.
171 * Instead we use a Message + Handler to relay commands to the main
172 * thread, which updates the user-text View.
173 */
174 synchronized (mSurfaceHolder) {
175 mAppState = mode;
176
177 if (mAppState == AppState.RUNNING) {
178 Message msg = mHandler.obtainMessage();
179 Bundle b = new Bundle();
180 b.putString("text", "");
181 b.putInt("viz", GameView.INVISIBLE);
182 msg.setData(b);
183 mHandler.sendMessage(msg);
184 } else {
185 CharSequence str = "";
186 str = "Mode probably changed";
187
188 if (message != null) {
189 str = message + "\n" + str;
190 }
191
192 Message msg = mHandler.obtainMessage();
193 Bundle b = new Bundle();
194 b.putString("text", str.toString());
195 b.putInt("viz", GameView.VISIBLE);
196 msg.setData(b);
197 //mHandler.sendMessage(msg);
198 }
199 }
200 }
201
202 /* Callback invoked when the surface dimensions change. */
203 public void setSurfaceSize(int width, int height) {
204 // synchronized to make sure these all change atomically
205 synchronized (mSurfaceHolder) {
206 mCanvasWidth = width;
207 mCanvasHeight = height;
208
209 Log.i("Blackjack", "width: "+mCanvasWidth+", height: "+mCanvasHeight);
210 }
211 }
212
213 /**
214 * Resumes from a pause.
215 */
216 public void unpause() {
217 // Move the real time clock up to now
218 synchronized (mSurfaceHolder) {
219 mLastTime = System.currentTimeMillis() + 100;
220 }
221 setState(AppState.RUNNING);
222 }
223
224 /**
225 * Handles a key-down event.
226 *
227 * @param keyCode the key that was pressed
228 * @param msg the original event object
229 * @return true
230 */
231 boolean doKeyDown(int keyCode, KeyEvent msg) {
232 synchronized (mSurfaceHolder) {
233 boolean okStart = false;
234 if (keyCode == KeyEvent.KEYCODE_DPAD_UP) okStart = true;
235 if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) okStart = true;
236 if (keyCode == KeyEvent.KEYCODE_S) okStart = true;
237
238 if (okStart
239 && (mAppState == AppState.READY || mAppState == AppState.LOSE || mAppState == AppState.WIN)) {
240 // ready-to-start -> start
241 doStart();
242 return true;
243 } else if (mAppState == AppState.PAUSE && okStart) {
244 // paused -> running
245 unpause();
246 return true;
247 } else if (mAppState == AppState.RUNNING) {
248 return true;
249 }
250
251 return false;
252 }
253 }
254
255 /**
256 * Handles a key-up event.
257 *
258 * @param keyCode the key that was pressed
259 * @param msg the original event object
260 * @return true if the key was handled and consumed, or else false
261 */
262 boolean doKeyUp(int keyCode, KeyEvent msg) {
263 boolean handled = false;
264
265 synchronized (mSurfaceHolder) {
266 if (mAppState == AppState.RUNNING) {
267 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
268 || keyCode == KeyEvent.KEYCODE_SPACE) {
269 handled = true;
270 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
271 || keyCode == KeyEvent.KEYCODE_Q
272 || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
273 || keyCode == KeyEvent.KEYCODE_W) {
274 handled = true;
275 }
276 }
277 }
278
279 return handled;
280 }
281
282 /**
283 * Draws the ship, fuel/speed bars, and background to the provided
284 * Canvas.
285 */
286 private void doDraw(Canvas canvas) {
287 canvas.drawColor(Color.BLACK);
288
289 mTextPaint.setTextSize(12);
290 Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
291
292 String text = "Your Total: ";
293 canvas.drawText(text, 40-mTextPaint.measureText(text)/2, 450-(metrics.ascent+metrics.descent)/2, mTextPaint);
294
295 text = "Dealer Total: ";
296 canvas.drawText(text, 125-mTextPaint.measureText(text)/2, 450-(metrics.ascent+metrics.descent)/2, mTextPaint);
297
298 text = mPlayerWins+" wins - "+mPlayerLosses+" losses";
299 canvas.drawText(text, 270-mTextPaint.measureText(text)/2, 450-(metrics.ascent+metrics.descent)/2, mTextPaint);
300 }
301
302 /**
303 * Figures the lander state (x, y, fuel, ...) based on the passage of
304 * realtime. Does not invalidate(). Called at the start of draw().
305 * Detects the end-of-game and sets the UI to the next state.
306 */
307 private void updatePhysics() {
308 long now = System.currentTimeMillis();
309
310 // Do nothing if mLastTime is in the future.
311 // This allows the game-start to delay the start of the physics
312 // by 100ms or whatever.
313 if (mLastTime > now) return;
314
315 // DO SHIT HERE
316
317 mLastTime = now+50;
318 }
319 }
320
321 /** Handle to the application context, used to e.g. fetch Drawables. */
322 private Context mContext;
323
324 /** Pointer to the text view to display "Paused.." etc. */
325 private TextView mStatusText;
326
327 /** The thread that actually draws the animation */
328 private DrawingThread thread;
329
330 public GameView(Context context, AttributeSet attrs) {
331 super(context, attrs);
332
333 // register our interest in hearing about changes to our surface
334 SurfaceHolder holder = getHolder();
335 holder.addCallback(this);
336
337 // create thread only; it's started in surfaceCreated()
338 thread = new DrawingThread(holder, context, new Handler() {
339 @Override
340 public void handleMessage(Message m) {
341 mStatusText.setVisibility(m.getData().getInt("viz"));
342 mStatusText.setText(m.getData().getString("text"));
343 }
344 });
345
346 setFocusable(true); // make sure we get key events
347 }
348
349 public void newRound() {
350 thread.mGameState = GameState.YOUR_TURN;
351 }
352
353 @Override public boolean onTouchEvent(MotionEvent event) {
354 Log.i("Blackjack", "Detected touch event");
355
356 if(event.getAction() == MotionEvent.ACTION_UP) {
357 Log.i("Blackjack", "Detected UP touch action");
358 switch(thread.mGameState) {
359 case YOUR_TURN:
360 Log.i("Blackjack", "Player's turn");
361 break;
362 case COMP_TURN:
363 Log.i("Blackjack", "Computer's turn");
364 break;
365 }
366 }else if(event.getAction() == MotionEvent.ACTION_DOWN) {
367
368 }
369
370 return true;
371 }
372
373 /**
374 * Fetches the animation thread corresponding to this LunarView.
375 *
376 * @return the animation thread
377 */
378 public DrawingThread getThread() {
379 return thread;
380 }
381
382 /**
383 * Standard override to get key-press events.
384 */
385 @Override
386 public boolean onKeyDown(int keyCode, KeyEvent msg) {
387 return thread.doKeyDown(keyCode, msg);
388 }
389
390 /**
391 * Standard override for key-up. We actually care about these, so we can
392 * turn off the engine or stop rotating.
393 */
394 @Override
395 public boolean onKeyUp(int keyCode, KeyEvent msg) {
396 return thread.doKeyUp(keyCode, msg);
397 }
398
399 /**
400 * Standard window-focus override. Notice focus lost so we can pause on
401 * focus lost. e.g. user switches to take a call.
402 */
403 @Override
404 public void onWindowFocusChanged(boolean hasWindowFocus) {
405 if (!hasWindowFocus) thread.pause();
406 }
407
408 /**
409 * Installs a pointer to the text view used for messages.
410 */
411 public void setTextView(TextView textView) {
412 mStatusText = textView;
413 }
414
415 /* Callback invoked when the surface dimensions change. */
416 public void surfaceChanged(SurfaceHolder holder, int format, int width,
417 int height) {
418 thread.setSurfaceSize(width, height);
419 }
420
421 /*
422 * Callback invoked when the Surface has been created and is ready to be
423 * used.
424 */
425 public void surfaceCreated(SurfaceHolder holder) {
426 // start the thread here so that we don't busy-wait in run()
427 // waiting for the surface to be created
428 thread.setRunning(true);
429
430 //if(thread.mAppState == AppState.PAUSE)
431 //thread.unpause();
432 //else
433 thread.start();
434 }
435
436 /*
437 * Callback invoked when the Surface has been destroyed and must no longer
438 * be touched. WARNING: after this method returns, the Surface/Canvas must
439 * never be touched again!
440 */
441 public void surfaceDestroyed(SurfaceHolder holder) {
442 // we have to tell thread to shut down & wait for it to finish, or else
443 // it might touch the Surface after we return and explode
444 boolean retry = true;
445 thread.setRunning(false);
446 while (retry) {
447 try {
448 thread.join();
449 retry = false;
450 } catch (InterruptedException e) {
451 }
452 }
453 }
454}
Note: See TracBrowser for help on using the repository browser.