如何在这个游戏中添加保存选项和其他一些东西



我已经问过这个问题,但因为不清楚,我想人们都在回避。我不想所有这些事情都一起完成,我只想其中一个,特别是保存游戏。我需要知道编码是如何的,这样我就可以研究它并从中学习。

我想编辑这个游戏以获得一些编程经验。我想增加挽救比赛、总胜和总输,还想在比赛中使用默认选项,以及改变球的速度和允许的时间。

请告诉我你会怎么做,这样我在做的时候就可以有一个参考,以确保我做对了。

安卓清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.deitel.cannongame" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" 
android:label="@string/app_name" android:debuggable="true">
<activity android:name=".CannonGame" 
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10"/>
</manifest> 

大炮游戏java

// CannonGame.java
// Main Activity for the Cannon Game app.
package com.deitel.cannongame;
import android.app.Activity;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
public class CannonGame extends Activity
{
private GestureDetector gestureDetector; // listens for double taps
private CannonView cannonView; // custom view to display the game
// called when the app first launches
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); // call super's onCreate method
setContentView(R.layout.main); // inflate the layout
// get the CannonView
cannonView = (CannonView) findViewById(R.id.cannonView);
// initialize the GestureDetector
gestureDetector = new GestureDetector(this, gestureListener);
// allow volume keys to set game volume
setVolumeControlStream(AudioManager.STREAM_MUSIC);
} // end method onCreate
// when the app is pushed to the background, pause it
@Override
public void onPause()
{
super.onPause(); // call the super method
cannonView.stopGame(); // terminates the game
} // end method onPause
// release resources
@Override
protected void onDestroy()
{
super.onDestroy();
cannonView.releaseResources();
} // end method onDestroy
// called when the user touches the screen in this Activity
@Override
public boolean onTouchEvent(MotionEvent event)
{
// get int representing the type of action which caused this event
int action = event.getAction();
// the user user touched the screen or dragged along the screen
if (action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_MOVE)
{
cannonView.alignCannon(event); // align the cannon
} // end if
// call the GestureDetector's onTouchEvent method
return gestureDetector.onTouchEvent(event);
} // end method onTouchEvent
// listens for touch events sent to the GestureDetector
SimpleOnGestureListener gestureListener = new SimpleOnGestureListener()
{
// called when the user double taps the screen
@Override
public boolean onDoubleTap(MotionEvent e)
{
cannonView.fireCannonball(e); // fire the cannonball
return true; // the event was handled
} // end method onDoubleTap
}; // end gestureListener
} // end class CannonGame

CannonView java

// CannonView.java
// Displays the Cannon Game
package com.deitel.cannongame;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CannonView extends SurfaceView 
implements SurfaceHolder.Callback
{
private CannonThread cannonThread; // controls the game loop
private Activity activity; // to display Game Over dialog in GUI thread
private boolean dialogIsDisplayed = false;   
// constants for game play
public static final int TARGET_PIECES = 7; // sections in the target
public static final int MISS_PENALTY = 2; // seconds deducted on a miss
public static final int HIT_REWARD = 3; // seconds added on a hit
// variables for the game loop and tracking statistics
private boolean gameOver; // is the game over?
private double timeLeft; // the amount of time left in seconds
private int shotsFired; // the number of shots the user has fired
private double totalElapsedTime; // the number of seconds elapsed
// variables for the blocker and target
private Line blocker; // start and end points of the blocker
private int blockerDistance; // blocker distance from left
private int blockerBeginning; // blocker distance from top
private int blockerEnd; // blocker bottom edge distance from top
private int initialBlockerVelocity; // initial blocker speed multiplier
private float blockerVelocity; // blocker speed multiplier during game
private Line target; // start and end points of the target
private int targetDistance; // target distance from left
private int targetBeginning; // target distance from top
private double pieceLength; // length of a target piece
private int targetEnd; // target bottom's distance from top
private int initialTargetVelocity; // initial target speed multiplier
private float targetVelocity; // target speed multiplier during game
private int lineWidth; // width of the target and blocker
private boolean[] hitStates; // is each target piece hit?
private int targetPiecesHit; // number of target pieces hit (out of 7)
// variables for the cannon and cannonball
private Point cannonball; // cannonball image's upper-left corner
private int cannonballVelocityX; // cannonball's x velocity
private int cannonballVelocityY; // cannonball's y velocity
private boolean cannonballOnScreen; // is the cannonball on the screen
private int cannonballSpeed; // cannonball speed
private int cannonBaseRadius; // cannon base radius
private int cannonLength; // cannon barrel length
private Point barrelEnd; // the endpoint of the cannon's barrel
private int cannonballRadius; // cannonball radius
private int screenWidth; // width of the screen
private int screenHeight; // height of the screen
// constants and variables for managing sounds
private static final int TARGET_SOUND_ID = 0;
private static final int CANNON_SOUND_ID = 1;
private static final int BLOCKER_SOUND_ID = 2;
private SoundPool soundPool; // plays sound effects
private Map<Integer, Integer> soundMap; // maps IDs to SoundPool
// Paint variables used when drawing each item on the screen
private Paint textPaint; // Paint used to draw text
private Paint cannonballPaint; // Paint used to draw the cannonball
private Paint cannonPaint; // Paint used to draw the cannon
private Paint blockerPaint; // Paint used to draw the blocker
private Paint targetPaint; // Paint used to draw the target
private Paint backgroundPaint; // Paint used to clear the drawing area
// public constructor
public CannonView(Context context, AttributeSet attrs)
{
super(context, attrs); // call super's constructor
activity = (Activity) context; 
// register SurfaceHolder.Callback listener
getHolder().addCallback(this); 
// initialize Lines and points representing game items
blocker = new Line(); // create the blocker as a Line
target = new Line(); // create the target as a Line
cannonball = new Point(); // create the cannonball as a point
// initialize hitStates as a boolean array
hitStates = new boolean[TARGET_PIECES];
// initialize SoundPool to play the app's three sound effects
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
// create Map of sounds and pre-load sounds
soundMap = new HashMap<Integer, Integer>(); // create new HashMap
soundMap.put(TARGET_SOUND_ID,
soundPool.load(context, R.raw.target_hit, 1));
soundMap.put(CANNON_SOUND_ID,
soundPool.load(context, R.raw.cannon_fire, 1));
soundMap.put(BLOCKER_SOUND_ID,
soundPool.load(context, R.raw.blocker_hit, 1));
// construct Paints for drawing text, cannonball, cannon,
// blocker and target; these are configured in method onSizeChanged
textPaint = new Paint(); // Paint for drawing text
cannonPaint = new Paint(); // Paint for drawing the cannon
cannonballPaint = new Paint(); // Paint for drawing a cannonball
blockerPaint = new Paint(); // Paint for drawing the blocker
targetPaint = new Paint(); // Paint for drawing the target
backgroundPaint = new Paint(); // Paint for drawing the target
} // end CannonView constructor
// called by surfaceChanged when the size of the SurfaceView changes,
// such as when it's first added to the View hierarchy
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
screenWidth = w; // store the width
screenHeight = h; // store the height
cannonBaseRadius = h / 18; // cannon base radius 1/18 screen height
cannonLength = w / 8; // cannon length 1/8 screen width
cannonballRadius = w / 36; // cannonball radius 1/36 screen width
cannonballSpeed = w * 3 / 2; // cannonball speed multiplier
lineWidth = w / 24; // target and blocker 1/24 screen width
// configure instance variables related to the blocker
blockerDistance = w * 5 / 8; // blocker 5/8 screen width from left
blockerBeginning = h / 8; // distance from top 1/8 screen height
blockerEnd = h * 3 / 8; // distance from top 3/8 screen height
initialBlockerVelocity = h / 2; // initial blocker speed multiplier
blocker.start = new Point(blockerDistance, blockerBeginning);
blocker.end = new Point(blockerDistance, blockerEnd);
// configure instance variables related to the target
targetDistance = w * 7 / 8; // target 7/8 screen width from left
targetBeginning = h / 8; // distance from top 1/8 screen height
targetEnd = h * 7 / 8; // distance from top 7/8 screen height
pieceLength = (targetEnd - targetBeginning) / TARGET_PIECES;
initialTargetVelocity = -h / 4; // initial target speed multiplier
target.start = new Point(targetDistance, targetBeginning);
target.end = new Point(targetDistance, targetEnd);
// endpoint of the cannon's barrel initially points horizontally
barrelEnd = new Point(cannonLength, h / 2);
// configure Paint objects for drawing game elements
textPaint.setTextSize(w / 20); // text size 1/20 of screen width
textPaint.setAntiAlias(true); // smoothes the text
cannonPaint.setStrokeWidth(lineWidth * 1.5f); // set line thickness
blockerPaint.setStrokeWidth(lineWidth); // set line thickness      
targetPaint.setStrokeWidth(lineWidth); // set line thickness       
backgroundPaint.setColor(Color.WHITE); // set background color
newGame(); // set up and start a new game
} // end method onSizeChanged
// reset all the screen elements and start a new game
public void newGame()
{
// set every element of hitStates to false--restores target pieces
for (int i = 0; i < TARGET_PIECES; ++i)
hitStates[i] = false;
targetPiecesHit = 0; // no target pieces have been hit
blockerVelocity = initialBlockerVelocity; // set initial velocity
targetVelocity = initialTargetVelocity; // set initial velocity
timeLeft = 10; // start the countdown at 10 seconds
cannonballOnScreen = false; // the cannonball is not on the screen
shotsFired = 0; // set the initial number of shots fired
totalElapsedTime = 0.0; // set the time elapsed to zero
blocker.start.set(blockerDistance, blockerBeginning);
blocker.end.set(blockerDistance, blockerEnd);
target.start.set(targetDistance, targetBeginning);
target.end.set(targetDistance, targetEnd);
if (gameOver)
{
gameOver = false; // the game is not over
cannonThread = new CannonThread(getHolder());
cannonThread.start();
} // end if
} // end method newGame
// called repeatedly by the CannonThread to update game elements
private void updatePositions(double elapsedTimeMS)
{
double interval = elapsedTimeMS / 1000.0; // convert to seconds
if (cannonballOnScreen) // if there is currently a shot fired
{
// update cannonball position
cannonball.x += interval * cannonballVelocityX;
cannonball.y += interval * cannonballVelocityY;
// check for collision with blocker
if (cannonball.x + cannonballRadius > blockerDistance && 
cannonball.x - cannonballRadius < blockerDistance &&
cannonball.y + cannonballRadius > blocker.start.y &&
cannonball.y - cannonballRadius < blocker.end.y)
{
cannonballVelocityX *= -1; // reverse cannonball's direction
timeLeft -= MISS_PENALTY; // penalize the user
// play blocker sound
soundPool.play(soundMap.get(BLOCKER_SOUND_ID), 1, 1, 1, 0, 1f);
} // end if
// check for collisions with left and right walls
else if (cannonball.x + cannonballRadius > screenWidth || 
cannonball.x - cannonballRadius < 0)
cannonballOnScreen = false; // remove cannonball from screen
// check for collisions with top and bottom walls
else if (cannonball.y + cannonballRadius > screenHeight || 
cannonball.y - cannonballRadius < 0)
cannonballOnScreen = false; // make the cannonball disappear
// check for cannonball collision with target
else if (cannonball.x + cannonballRadius > targetDistance && 
cannonball.x - cannonballRadius < targetDistance && 
cannonball.y + cannonballRadius > target.start.y &&
cannonball.y - cannonballRadius < target.end.y)
{
// determine target section number (0 is the top)
int section = 
(int) ((cannonball.y - target.start.y) / pieceLength);
// check if the piece hasn't been hit yet
if ((section >= 0 && section < TARGET_PIECES) && 
!hitStates[section])
{
hitStates[section] = true; // section was hit
cannonballOnScreen = false; // remove cannonball
timeLeft += HIT_REWARD; // add reward to remaining time
// play target hit sound
soundPool.play(soundMap.get(TARGET_SOUND_ID), 1,
1, 1, 0, 1f);
// if all pieces have been hit
if (++targetPiecesHit == TARGET_PIECES)
{
cannonThread.setRunning(false);
showGameOverDialog(R.string.win); // show winning dialog
gameOver = true; // the game is over
} // end if
} // end if
} // end else if
} // end if
// update the blocker's position
double blockerUpdate = interval * blockerVelocity;
blocker.start.y += blockerUpdate;
blocker.end.y += blockerUpdate;
// update the target's position
double targetUpdate = interval * targetVelocity;
target.start.y += targetUpdate;
target.end.y += targetUpdate;
// if the blocker hit the top or bottom, reverse direction
if (blocker.start.y < 0 || blocker.end.y > screenHeight)
blockerVelocity *= -1;
// if the target hit the top or bottom, reverse direction
if (target.start.y < 0 || target.end.y > screenHeight)
targetVelocity *= -1;
timeLeft -= interval; // subtract from time left
// if the timer reached zero
if (timeLeft <= 0.0)
{
timeLeft = 0.0;
gameOver = true; // the game is over
cannonThread.setRunning(false);
showGameOverDialog(R.string.lose); // show the losing dialog
} // end if
} // end method updatePositions
// fires a cannonball
public void fireCannonball(MotionEvent event)
{
if (cannonballOnScreen) // if a cannonball is already on the screen
return; // do nothing
double angle = alignCannon(event); // get the cannon barrel's angle
// move the cannonball to be inside the cannon
cannonball.x = cannonballRadius; // align x-coordinate with cannon
cannonball.y = screenHeight / 2; // centers ball vertically
// get the x component of the total velocity
cannonballVelocityX = (int) (cannonballSpeed * Math.sin(angle));
// get the y component of the total velocity
cannonballVelocityY = (int) (-cannonballSpeed * Math.cos(angle));
cannonballOnScreen = true; // the cannonball is on the screen
++shotsFired; // increment shotsFired
// play cannon fired sound
soundPool.play(soundMap.get(CANNON_SOUND_ID), 1, 1, 1, 0, 1f);
} // end method fireCannonball
// aligns the cannon in response to a user touch
public double alignCannon(MotionEvent event)
{
// get the location of the touch in this view
Point touchPoint = new Point((int) event.getX(), (int) event.getY());
// compute the touch's distance from center of the screen
// on the y-axis
double centerMinusY = (screenHeight / 2 - touchPoint.y);
double angle = 0; // initialize angle to 0
// calculate the angle the barrel makes with the horizontal
if (centerMinusY != 0) // prevent division by 0
angle = Math.atan((double) touchPoint.x / centerMinusY);
// if the touch is on the lower half of the screen
if (touchPoint.y > screenHeight / 2)
angle += Math.PI; // adjust the angle
// calculate the endpoint of the cannon barrel
barrelEnd.x = (int) (cannonLength * Math.sin(angle));
barrelEnd.y = 
(int) (-cannonLength * Math.cos(angle) + screenHeight / 2);
return angle; // return the computed angle
} // end method alignCannon
// draws the game to the given Canvas
public void drawGameElements(Canvas canvas)
{
// clear the background
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), 
backgroundPaint);
// display time remaining
canvas.drawText(getResources().getString(
R.string.time_remaining_format, timeLeft), 30, 50, textPaint);
// if a cannonball is currently on the screen, draw it
if (cannonballOnScreen)
canvas.drawCircle(cannonball.x, cannonball.y, cannonballRadius,
cannonballPaint);
// draw the cannon barrel
canvas.drawLine(0, screenHeight / 2, barrelEnd.x, barrelEnd.y,
cannonPaint);
// draw the cannon base
canvas.drawCircle(0, (int) screenHeight / 2,
(int) cannonBaseRadius, cannonPaint);
// draw the blocker
canvas.drawLine(blocker.start.x, blocker.start.y, blocker.end.x,
blocker.end.y, blockerPaint);
Point currentPoint = new Point(); // start of current target section
// initialize curPoint to the starting point of the target
currentPoint.x = target.start.x;
currentPoint.y = target.start.y;
// draw the target
for (int i = 1; i <= TARGET_PIECES; ++i)
{
// if this target piece is not hit, draw it
if (!hitStates[i - 1])
{
// alternate coloring the pieces yellow and blue
if (i % 2 == 0)
targetPaint.setColor(Color.YELLOW);
else
targetPaint.setColor(Color.BLUE);
canvas.drawLine(currentPoint.x, currentPoint.y, target.end.x,
(int) (currentPoint.y + pieceLength), targetPaint);
} // end if
// move curPoint to the start of the next piece
currentPoint.y += pieceLength;
} // end for
} // end method drawGameElements
// display an AlertDialog when the game ends
private void showGameOverDialog(int messageId)
{
// create a dialog displaying the given String
final AlertDialog.Builder dialogBuilder = 
new AlertDialog.Builder(getContext());
dialogBuilder.setTitle(getResources().getString(messageId));
dialogBuilder.setCancelable(false);
// display number of shots fired and total time elapsed
dialogBuilder.setMessage(getResources().getString(
R.string.results_format, shotsFired, totalElapsedTime));
dialogBuilder.setPositiveButton(R.string.reset_game,
new DialogInterface.OnClickListener()
{
// called when "Reset Game" Button is pressed
public void onClick(DialogInterface dialog, int which)
{
dialogIsDisplayed = false;
newGame(); // set up and start a new game
} // end method onClick
} // end anonymous inner class
); // end call to setPositiveButton
activity.runOnUiThread(
new Runnable() {
public void run()
{
dialogIsDisplayed = true;
dialogBuilder.show(); // display the dialog
} // end method run
} // end Runnable
); // end call to runOnUiThread
} // end method showGameOverDialog
// stops the game
public void stopGame()
{
if (cannonThread != null)
cannonThread.setRunning(false);
} // end method stopGame
// releases resources; called by CannonGame's onDestroy method 
public void releaseResources()
{
soundPool.release(); // release all resources used by the SoundPool
soundPool = null; 
} // end method releaseResources
// called when surface changes size
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height)
{
} // end method surfaceChanged
// called when surface is first created
public void surfaceCreated(SurfaceHolder holder)
{
if (!dialogIsDisplayed)
{
cannonThread = new CannonThread(holder);
cannonThread.setRunning(true);
cannonThread.start(); // start the game loop thread
} // end if
} // end method surfaceCreated
// called when the surface is destroyed
public void surfaceDestroyed(SurfaceHolder holder)
{
// ensure that thread terminates properly
boolean retry = true;
cannonThread.setRunning(false);
while (retry)
{
try
{
cannonThread.join();
retry = false;
} // end try
catch (InterruptedException e)
{
} // end catch
} // end while
} // end method surfaceDestroyed
// Thread subclass to control the game loop
private class CannonThread extends Thread
{
private SurfaceHolder surfaceHolder; // for manipulating canvas
private boolean threadIsRunning = true; // running by default
// initializes the surface holder
public CannonThread(SurfaceHolder holder)
{
surfaceHolder = holder;
setName("CannonThread");
} // end constructor
// changes running state
public void setRunning(boolean running)
{
threadIsRunning = running;
} // end method setRunning
// controls the game loop
@Override
public void run()
{
Canvas canvas = null; // used for drawing
long previousFrameTime = System.currentTimeMillis(); 
while (threadIsRunning)
{
try
{
canvas = surfaceHolder.lockCanvas(null);               
// lock the surfaceHolder for drawing
synchronized(surfaceHolder)
{
long currentTime = System.currentTimeMillis();
double elapsedTimeMS = currentTime - previousFrameTime;
totalElapsedTime += elapsedTimeMS / 1000.00; 
updatePositions(elapsedTimeMS); // update game state
drawGameElements(canvas); // draw 
previousFrameTime = currentTime; // update previous time
} // end synchronized block
} // end try
finally
{
if (canvas != null) 
surfaceHolder.unlockCanvasAndPost(canvas);
} // end finally
} // end while
} // end method run
} // end nested class CannonThread
} // end class CannonView

Line java

// Line.java
// Class Line represents a line with two endpoints.
package com.deitel.cannongame;
import android.graphics.Point;
public class Line
{
public Point start; // starting Point
public Point end; // ending Point
// default constructor initializes Points to (0, 0)
public Line()
{
start = new Point(0, 0); // start Point
end = new Point(0, 0); // end Point
} // end method Line
} // end class Line

要保存分数,你必须保留它的记录。我在游戏中所做的是将它保存在SharedPreferences中,更多信息请点击此处:

http://developer.android.com/reference/android/content/SharedPreferences.html

我跟踪分数的方法是创建一个名为GameData的类(派生自Object,没有Activity),并为分数实现一个静态setter和getter,类似于以下内容:

public class GameData{
private static final String GAME_DATA_FILE_NAME = "GAME_DATA_FILE_NAME";
private static final String GAME_DATA_WINS = "GAME_DATA_WINS";
private static final String GAME_DATA_LOSSES = "GAME_DATA_LOSSES";
public static void updateGameWinResults(Context ctx, Boolean gameWon){
if(gameWon){
ctx.getSharedPreferences(GAME_DATA_FILE_NAME, Context.MODE_PRIVATE).edit().putInt(GAME_DATA_WINS, getGameWins(ctx) + 1).commit();
} else {
ctx.getSharedPreferences(GAME_DATA_FILE_NAME, Context.MODE_PRIVATE).edit().putInt(GAME_DATA_WINS, getGameLosses(ctx) + 1).commit();
}
}
public static int getGameWins(Context ctx)
SharedPreferences sp = c.getSharedPreferences(GAME_DATA_FILE_NAME, Context.MODE_PRIVATE);
return sp.getInt(GAME_DATA_WINS, 0);
}
public static int getGameLosses(Context ctx){
SharedPreferences sp = c.getSharedPreferences(GAME_DATA_FILE_NAME, Context.MODE_PRIVATE);
return sp.getInt(GAME_DATA_LOSSES, 0);
}
}

当然,在你的代码中,从活动中的任何地方(尽管我建议你在游戏结束并试图保存数据时将保存部分放在某个地方),你只需调用:

GameData.updateGameWinResults(this,false); // if they lost... give it a true if they won

为了获得胜利,做:

int wins = GameData.getGameWins(this);

或损失:

int losses = GameData.getGameLosses(this);

或者甚至可以获得全部比赛(假设只有输赢):

int gamesPlayed = GameData.getGameWins(this) + GameData.getGameLosses(this);

至于保存选项,使用相同的机制也很容易实现。有道理吗?

但这个答案开始变得很长了。如果你对其他部分感到困惑(在你尝试了ppl的解决方案后,我建议把它作为一个单独的问题来问,只是关于其他选项。这样,你就有机会通过投票和接受,给Stack Overflow用户更多的分数)

最新更新