source: advance-wars/src/com/example/advancewars/GameView.java@ 90838a1

Last change on this file since 90838a1 was dd3e793, checked in by sdanshin <devnull@…>, 14 years ago

Created a blank window where the main menu should be. Touching the screen shows the screen with the blue and green grid.

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