Optimierung oder Erstellung von 60 FPS für Android-Spiele, die mit Java geschrieben wurden
Posted: 18 Jan 2025, 20:46
Ich möchte ein eigenes Spiel mit Java in Android Studio erstellen. Ich habe eine Idee: Online-Pixel-Shooter machen. Übrigens, als ich die Karte hinzugefügt habe, egal welche Größe ich habe, habe ich „Verzögerungen“, da ich mit 15 FPS teste. Ich habe SurfaceView verwendet, um dieses Spiel zu erstellen. Das Spiel läuft mit 60 FPS, wobei die update()-Methode die Spiellogik verwaltet und die draw()-Methode das Spiel rendert.
update()
drawstring>
Der Spieler kann sich mit einem Joystick bewegen, Kugeln abfeuern und einen vorübergehenden Geschwindigkeitsschub aktivieren. Die TileMap-Klasse übernimmt das Kartenrendering
und die Kameraklasse sorgen dafür, dass der Player auf dem Bildschirm zentriert bleibt.
Es gibt eine Hauptmethode, die den gesamten Code startet:
Mein Versuch, den Code zu beschleunigen:
update()
Code: Select all
private void update() {
// Оновлення стану прискорення
if (isAccelerated && System.currentTimeMillis() - accelerationStartTime > ACCELERATION_DURATION) {
isAccelerated = false;
playerSpeed = DEFAULT_PLAYER_SPEED;
}
// Оновлення позиції гравця
PointF direction = joystick.getDirection();
int newPlayerX = playerX + (int) (direction.x * playerSpeed);
int newPlayerY = playerY + (int) (direction.y * playerSpeed);
if (!tileMap.isWall(newPlayerX / tileMap.getTileSize(), newPlayerY / tileMap.getTileSize())) {
playerX = newPlayerX;
playerY = newPlayerY;
}
// Оновлення стрільби
if (isShooting) {
player.shoot();
}
camera.update(playerX, playerY);
}
Code: Select all
private void draw() {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
try {
canvas.drawColor(0xFFFFFFFF);
tileMap.draw(canvas, camera.getX(), camera.getY());
canvas.drawBitmap(
playerBitmap,
playerX - camera.getX(),
playerY - camera.getY(),
null
);
// Малювання кулі
if (isShooting) {
canvas.drawBitmap(
bulletBitmap,
bulletX - camera.getX(),
bulletY - camera.getY(),
null
);
}
joystick.draw(canvas);
// Малювання кнопок
shootButton.draw(canvas);
accelerateButton.draw(canvas);
} finally {
getHolder().unlockCanvasAndPost(canvas);
}
}
}
Code: Select all
//TileMap.java
package com.ccs.mempixel.game;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.SparseArray;
import com.ccs.mempixel.R;
public class TileMap {
private int[][] map; // Масив, що описує карту
private Bitmap[] tileImages; // Зображення для кожного типу тайла
private int tileSize; // Розмір одного тайла (наприклад, 32x32 пікселів)
private Bitmap cachedVisibleMap; // Кешована видима частина карти
private int cachedCameraX, cachedCameraY; // Кешовані координати камери
private SparseArray collisionCache = new SparseArray();
public boolean isWall(int x, int y) {
int key = y * map[0].length + x; // Унікальний ключ для кешування
Boolean cachedResult = collisionCache.get(key);
if (cachedResult != null) {
return cachedResult;
}
boolean result;
if (y >= 0 && y < map.length && x >= 0 && x < map[y].length) {
result = map[y][x] == 1; // 1 - стіна
} else {
result = true; // Якщо координати поза межами карти, вважаємо це стіною
}
collisionCache.put(key, result);
return result;
}
public void draw(Canvas canvas, int cameraX, int cameraY) {
if (cachedVisibleMap == null || cachedCameraX != cameraX || cachedCameraY != cameraY) {
// Якщо кеш неактуальний, створюємо новий
cachedCameraX = cameraX;
cachedCameraY = cameraY;
cachedVisibleMap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas cacheCanvas = new Canvas(cachedVisibleMap);
int screenWidth = canvas.getWidth();
int screenHeight = canvas.getHeight();
int startX = Math.max(0, cameraX / tileSize);
int startY = Math.max(0, cameraY / tileSize);
int endX = Math.min(map[0].length, (cameraX + screenWidth) / tileSize + 1);
int endY = Math.min(map.length, (cameraY + screenHeight) / tileSize + 1);
for (int y = startY; y < endY; y++) {
for (int x = startX; x < endX; x++) {
int tileType = map[y][x];
if (tileType >= 0 && tileType < tileImages.length) {
cacheCanvas.drawBitmap(
tileImages[tileType],
x * tileSize - cameraX,
y * tileSize - cameraY,
null
);
}
}
}
}
// Малюємо кешовану видиму частину карти
canvas.drawBitmap(cachedVisibleMap, 0, 0, null);
}
public TileMap(Context context, int[][] map, int tileSize) {
this.map = map;
this.tileSize = tileSize;
// Завантаження зображень для тайлів
tileImages = new Bitmap[2]; // 0 - підлога, 1 - стіна
tileImages[0] = BitmapFactory.decodeResource(context.getResources(), R.drawable.floor1); // Підлога
tileImages[1] = BitmapFactory.decodeResource(context.getResources(), R.drawable.wall1); // Стіна
// Масштабування зображень до розміру тайла
for (int i = 0; i < tileImages.length; i++) {
tileImages[i] = Bitmap.createScaledBitmap(tileImages[i], tileSize, tileSize, false);
}
}
// Отримання розмірів карти в пікселях
public int getMapWidth() {
return map[0].length * tileSize;
}
public int getMapHeight() {
return map.length * tileSize;
}
// Отримання розміру тайла
public int getTileSize() {
return tileSize;
}
}
Code: Select all
//Camera.java
package com.ccs.mempixel.game;
public class Camera {
private int x, y; // Позиція камери
private int screenWidth, screenHeight; // Розміри екрану
private int mapWidth, mapHeight; // Розміри карти
private float smoothSpeed = 0.1f; // Швидкість плавного переміщення
private int lastPlayerX = -1;
private int lastPlayerY = -1;
public Camera(int screenWidth, int screenHeight, int mapWidth, int mapHeight) {
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
}
// Оновлення позиції камери (слідкування за гравцем)
public void update(int playerX, int playerY) {
if (lastPlayerX != playerX || lastPlayerY != playerY) {
lastPlayerX = playerX;
lastPlayerY = playerY;
int targetX = playerX - screenWidth / 2;
int targetY = playerY - screenHeight / 2;
// Плавне переміщення камери
x += (targetX - x) * smoothSpeed;
y += (targetY - y) * smoothSpeed;
// Обмеження руху камери
x = Math.max(0, Math.min(x, mapWidth - screenWidth));
y = Math.max(0, Math.min(y, mapHeight - screenHeight));
}
}
// Отримання позиції камери
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Code: Select all
@Override
public void run() {
long lastTime = System.nanoTime();
double nsPerUpdate = 1000000000.0 / 60.0; // 60 FPS
double delta = 0;
while (isRunning) {
long now = System.nanoTime();
delta += (now - lastTime) / nsPerUpdate;
lastTime = now;
while (delta >= 1) {
update();
delta--;
}
draw();
}
}
- Hardwareleistung im Manifest hinzufügen:
Code: Select all
= 1) {
update();
delta--;
}
draw();
}
}
private void update() {
// Оновлення стану прискорення
if (isAccelerated && System.currentTimeMillis() - accelerationStartTime > ACCELERATION_DURATION) {
isAccelerated = false;
playerSpeed = DEFAULT_PLAYER_SPEED;
}
// Оновлення позиції гравця
PointF direction = joystick.getDirection();
int newPlayerX = playerX + (int) (direction.x * playerSpeed);
int newPlayerY = playerY + (int) (direction.y * playerSpeed);
if (!tileMap.isWall(newPlayerX / tileMap.getTileSize(), newPlayerY / tileMap.getTileSize())) {
playerX = newPlayerX;
playerY = newPlayerY;
}
// Оновлення стрільби
if (isShooting) {
player.shoot();
}
camera.update(playerX, playerY);
}
private void draw() {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
try {
canvas.drawColor(0xFFFFFFFF);
tileMap.draw(canvas, camera.getX(), camera.getY());
canvas.drawBitmap(
playerBitmap,
playerX - camera.getX(),
playerY - camera.getY(),
null
);
// Малювання кулі
if (isShooting) {
canvas.drawBitmap(
bulletBitmap,
bulletX - camera.getX(),
bulletY - camera.getY(),
null
);
}
joystick.draw(canvas);
// Малювання кнопок
shootButton.draw(canvas);
accelerateButton.draw(canvas);
} finally {
getHolder().unlockCanvasAndPost(canvas);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (accelerateButton.isTouched(touchX, touchY)) {
playerSpeed = ACCELERATED_PLAYER_SPEED;
isSpeedButtonPressed = true;
isShootButtonPressed = false;
return true;
}
if (shootButton.isTouched(touchX, touchY)) {
isShootButtonPressed = true;
isSpeedButtonPressed = false;
return true;
}
isSpeedButtonPressed = false;
isShootButtonPressed = false;
joystick.updateHandle(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
isSpeedButtonPressed = false;
isShootButtonPressed = false;
joystick.resetHandle();
break;
}
return true;
}
private Point getRandomFloorPosition(int[][] map) {
Random random = new Random();
int x, y;
// Шукаємо випадкову позицію, де є підлога (0)
do {
x = random.nextInt(map[0].length);
y = random.nextInt(map.length);
} while (map[y][x] != 0);
return new Point(x, y);
}
}