我正在制作的 Android 游戏有一个奇怪的问题。这是一款 2D 动作益智游戏;精灵和背景是简单的不透明图像,但需要快速流畅地移动。然而,我一直遇到一般的帧速率问题,但这个特殊问题让我很困惑。
问题是我可以启动我的游戏(在硬件上,Nexus 7)并让它以至少 60fps 的帧速率运行,然后关闭它并重新启动它,它将以 30-45 的速度运行。重复此过程,游戏通常以较慢的帧速率运行,但每 4 次(左右)尝试运行时非常流畅。
我是 Android 和一般编程的新手,所以我想知道是否有人猜测是什么可能导致重复运行时出现这种性能差异?如果有帮助,我可以编辑以包含一些代码,但这似乎更具理论性,我不确定与发布相关的内容。
谢谢。
编辑:
这是我的 onCreate 代码:
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Window;
import android.view.WindowManager;
import com.jag.framework.Audio;
import com.jag.framework.FileIO;
import com.jag.framework.Game;
import com.jag.framework.Graphics;
import com.jag.framework.Input;
import com.jag.framework.Screen;
public abstract class AndroidGame extends Activity implements Game {
AndroidFastRenderView renderView;
Graphics graphics;
Audio audio;
Input input;
FileIO fileIO;
Screen screen;
WakeLock wakeLock;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
int frameBufferWidth = isPortrait ? 800: 1200;
int frameBufferHeight = isPortrait ? 1200: 800;
Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth,
frameBufferHeight, Config.RGB_565);
float scaleX = (float) frameBufferWidth
/ getWindowManager().getDefaultDisplay().getWidth();
float scaleY = (float) frameBufferHeight
/ getWindowManager().getDefaultDisplay().getHeight();
renderView = new AndroidFastRenderView(this, frameBuffer);
graphics = new AndroidGraphics(getAssets(), frameBuffer);
fileIO = new AndroidFileIO(this);
audio = new AndroidAudio(this);
input = new AndroidInput(this, renderView, scaleX, scaleY);
screen = getInitScreen();
setContentView(renderView);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyGame");
}
@Override
public void onResume() {
super.onResume();
wakeLock.acquire();
screen.resume();
renderView.resume();
}
@Override
public void onPause() {
super.onPause();
wakeLock.release();
renderView.pause();
screen.pause();
if (isFinishing())
screen.dispose();
}
@Override
public Input getInput() {
return input;
}
@Override
public FileIO getFileIO() {
return fileIO;
}
@Override
public Graphics getGraphics() {
return graphics;
}
@Override
public Audio getAudio() {
return audio;
}
@Override
public void setScreen(Screen screen) {
if (screen == null)
throw new IllegalArgumentException("Screen must not be null");
this.screen.pause();
this.screen.dispose();
screen.resume();
screen.update(0);
this.screen = screen;
}
public Screen getCurrentScreen() {
return screen;
}
}
这是我的主要代码:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import com.jag.framework.Game;
import com.jag.framework.Graphics;
import com.jag.framework.Image;
import com.jag.framework.Input.TouchEvent;
import com.jag.framework.Screen;
public class GameScreen extends Screen {
enum GameState {
Ready, Running, Paused, GameOver
}
GameState state = GameState.Ready;
// Variable Setup
private static Scene scene;
public static int screenheight;
private ArrayList<Pieces> pieces;
//lanes:
int lane;
Paint paint, paint2;
Rect rect;
Image fore, rings1, rings2, base1, base2;
boolean recent, freeze, touch, pospressed, negpressed, wrongbutton;
int timepassed, difficulty, recentinterval, score;
Bitmap bitmap;
Canvas canvas2;
Rect foreg;
InputStream in;
AssetManager assets;
public GameScreen(Game game) {
super(game);
// Initialize game objects here
scene = new Scene(600);
screenheight = game.getGraphics().getHeight();
pieces = new ArrayList<Pieces>();
lane = 100;
recent = true;
Pieces p3 = new Pieces(lane, 940, true);
pieces.add(p3);
paint = new Paint();
paint.setTextSize(30);
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
paint2 = new Paint();
paint2.setColor(Color.WHITE);
paint2.setStyle(Style.FILL);
difficulty = 50;
recentinterval = 30;
timepassed = 0;
freeze = false;
fore = Assets.block;
rings1 = Assets.ringswhite;
rings2 = Assets.ringsblack;
base1 = Assets.basewhite;
base2 = Assets.baseblack;
wrongbutton = false;
score = 0;
}
@Override
public void update(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
// We have four separate update methods in this example.
// Depending on the state of the game, we call different update methods.
// Refer to Unit 3's code. We did a similar thing without separating the
// update methods.
if (state == GameState.Ready)
updateReady(touchEvents);
if (state == GameState.Running)
updateRunning(touchEvents, deltaTime);
if (state == GameState.Paused)
updatePaused(touchEvents);
if (state == GameState.GameOver)
updateGameOver(touchEvents);
}
private void updateReady(List<TouchEvent> touchEvents) {
// This example starts with a "Ready" screen.
// When the user touches the screen, the game begins.
// state now becomes GameState.Running.
// Now the updateRunning() method will be called!
if (touchEvents.size() > 0) {
game.getGraphics().clearScreen(Color.BLACK);
state = GameState.Running;
Assets.theme.stop();
}
}
private void updateRunning(List<TouchEvent> touchEvents, float deltaTime) {
// 1. All touch input is handled here:
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if ((event.type == TouchEvent.TOUCH_DRAGGED || event.type == TouchEvent.TOUCH_DOWN) && event.y <= 1000) {
touch = true;
scene.setLine(event.x);
}
//POSITIVE BUTTON
if (event.type == TouchEvent.TOUCH_DOWN && ((0 < event.x) && (event.x < 250)) && event.y > 1000) {
pospressed = true;
}
//NEGATIVE BUTTON
if (event.type == TouchEvent.TOUCH_DOWN && ((550 < event.x) && (event.x < 800)) && event.y > 1000) {
negpressed = true;
}
//ACTIVATES ALERT (reduce score)
if (event.type == TouchEvent.TOUCH_UP) {
touch = false;
pospressed = false;
negpressed = false;
}
}
// 2. Check miscellaneous events like death:
// if (livesLeft == 0) {
// state = GameState.GameOver;
// }
// 3. Call individual update() methods here.
// This is where all the game updates happen.
// For example, robot.update();
if (!freeze){
timepassed += 1;
if ((timepassed % recentinterval) == 0){
recent = false;
}
}
Random randomGenerator = new Random();
int randomInt = randomGenerator.nextInt(difficulty);
int randomInt2 = randomGenerator.nextInt(7);
boolean randomBool = randomGenerator.nextBoolean();
int chanceOfNewPiece = 8;
if ((randomInt < chanceOfNewPiece)&&!recent) {
Pieces p = new Pieces((randomInt2+1)*lane, 940, randomBool);
pieces.add(p);
recent = true;
}
Iterator<Pieces> it = pieces.iterator();
while (it.hasNext()) {
Pieces p = it.next();
if (p.isVisible()&&!p.wayback&&!freeze)
p.update();
else if (p.isVisible()&&!p.wayback&&freeze){
p.still();
}
else if (p.wayback && pospressed){
if (score > 10){
score -= 10;
}
if (score <= 10){
score = 0;
}
if (p.type){
it.remove();
freeze = false;
pospressed = false;
Assets.click.play(100);
}
if (!p.type){
freeze = false;
pospressed = false;
wrongbutton = true;
}
}
else if (p.wayback && negpressed){
if (score > 10){
score -= 10;
}
if (score <= 10){
score = 0;
}
if (p.type){
freeze = false;
negpressed = false;
wrongbutton = true;
}
if (!p.type){
it.remove();
freeze = false;
negpressed = false;
Assets.click.play(100);
}
}
else if (p.isVisible() && p.wayback && wrongbutton){
p.updateback();
}
else if (p.isVisible()&&p.wayback){
p.updateback();
freeze = true;
}
else if (p.y < 10){
score += 1;
it.remove();
freeze = false;
wrongbutton = false;
}
else {
// if (p.y > screenheight-281){
// game.getGraphics().clearScreen(Color.BLACK);
// state = GameState.GameOver;
// }
score = 0;
it.remove();
freeze = false;
wrongbutton = false;
}
}
// if (pieces.size() == 0) {
//
// game.getGraphics().clearScreen(Color.BLACK);
// state = GameState.GameOver;
// }
}
private void updatePaused(List<TouchEvent> touchEvents) {
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_UP) {
state = GameState.Running;
Assets.theme.stop();
}
}
}
private void updateGameOver(List<TouchEvent> touchEvents) {
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_UP) {
nullify();
game.setScreen(new MainMenuScreen(game));
return;
}
}
}
@Override
public void paint(float deltaTime) {
//Debug.startMethodTracing();
Graphics g = game.getGraphics();
// draw the game elements
if (state == GameState.Running){
int fingerx = scene.getLine();
g.drawRect(fingerx, 0, g.getWidth(), 1200, Color.BLACK);
g.drawRect(0, 0, fingerx, 1200, Color.WHITE);
if (touch == false){
g.drawRect(250, 1000, 302, 205, Color.RED);
}
g.drawScaledImage(base1, fingerx, 955, g.getWidth()-fingerx,
base2.getHeight(), fingerx, 0, g.getWidth()-fingerx, base2.getHeight());
g.drawScaledImage(base2, 0, 955, fingerx, base2.getHeight(), 0, 0, fingerx, base2.getHeight());
g.drawString(String.valueOf(score),
350, 1075, paint);
////g.drawImage(back, 0, 0);
// g.drawScaledImage(Assets.fore, fingerx, 0, g.getWidth()-fingerx,
// Assets.fore.getHeight(), fingerx, 0, g.getWidth()-fingerx, Assets.fore.getHeight());
////g.drawImage(Assets.topwhite, 0, 0);
//g.saveCanvas();
//g.drawTransRect(0, 0, fingerx, g.getHeight());
////g.drawImage(fore, 0, 0);
//g.drawImage(base2, 0, 955);
// g.drawScaledImage(Assets.back, 0, 0, fingerx, Assets.back.getHeight(), 0, 0, fingerx, base2.getHeight());
// g.drawScaledImage(base2, fingerx, 955, g.getWidth()-fingerx, base2.getHeight(),
// fingerx, 0, g.getWidth()-fingerx, base2.getHeight());
//g.drawCropped(base2);
////g.drawImage(rings2, 0, 950);
//g.restoreCanvas();
for (Pieces p : pieces){
if (p.type == true)
g.drawImage(Assets.pos, (p.x - 40), p.y);
if (p.type == false){
g.drawImage(Assets.neg, (p.x - 40), p.y);
}
}
}
// draw the UI
if (state == GameState.Ready)
drawReadyUI();
if (state == GameState.Running)
drawRunningUI();
if (state == GameState.Paused)
drawPausedUI();
if (state == GameState.GameOver)
drawGameOverUI();
//Debug.stopMethodTracing();
}
private void nullify() {
// Set all variables to null. You will be recreating them in the
// constructor.
paint = null;
scene = null;
pieces = null;
scene = null;
pieces = null;
paint2 = null;
Assets.theme = null;
Assets.click = null;
// Call garbage collector to clean up memory.
System.gc();
}
private void drawReadyUI() {
Graphics g = game.getGraphics();
g.drawARGB(155, 0, 0, 0);
g.drawString("TOUCH THE SCREEN YA DUMMY",
400, 300, paint);
}
private void drawRunningUI() {
//Graphics g = game.getGraphics();
}
private void drawPausedUI() {
Graphics g = game.getGraphics();
// Darken the entire screen so you can display the Paused screen.
g.drawRect(0, 0, 801, 1281, Color.BLACK);
g.drawString("HEY GUY IT'S PAUSED", 640, 300, paint);
}
private void drawGameOverUI() {
Graphics g = game.getGraphics();
g.drawRect(0, 0, 1281, 801, Color.BLACK);
g.drawString("GAME OVER BRO", 640, 300, paint);
}
@Override
public void pause() {
if (state == GameState.Running)
System.gc();
state = GameState.Paused;
}
@Override
public void resume() {
}
@Override
public void dispose() {
}
@Override
public void backButton() {
pause();
}
public static Scene getScene(){
return scene;
}
public void setScore(int i){
score += i;
}
}
我意识到这有点乱。这是我编码的第一件事。该框架来自kilobolt.com。让我知道我是否应该包括其他类/方法。