1. 项目概述
本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。
2. 项目结构
/src/main/java/com/example/runner/
├── MainAbilitySlice.java // 主界面
├── GameView.java // 游戏核心逻辑
├── Player.java // 玩家角色类
├── Obstacle.java // 障碍物类
└── resources/
├── base/
│ ├── layout/
│ │ └── ability_main.xml // 布局文件
│ └── media/
│ ├── player.png // 角色图片
│ ├── obstacle.png // 障碍物图片
│ ├── background.png // 背景图片
│ └── ground.png // 地面图片
3. 核心代码实现
3.1 玩家角色类(Player.java)
package com.example.runner;
import ohos.agp.utils.RectFloat;
public class Player {
private RectFloat rect; // 角色矩形区域
private float velocityY; // Y轴速度
private boolean isJumping; // 是否在跳跃
private boolean isDucking; // 是否在蹲下
public Player(float left, float top, float right, float bottom) {
this.rect = new RectFloat(left, top, right, bottom);
this.velocityY = 0;
this.isJumping = false;
this.isDucking = false;
}
// 跳跃
public void jump() {
if (!isJumping) {
velocityY = -25; // 向上的初速度
isJumping = true;
}
}
// 蹲下
public void duck() {
if (!isJumping) {
isDucking = true;
rect.modify(rect.left, rect.top + 20,
rect.right, rect.bottom);
}
}
// 站起
public void stand() {
isDucking = false;
rect.modify(rect.left, rect.top - 20,
rect.right, rect.bottom);
}
// 更新位置
public void update() {
if (isJumping) {
// 更新Y轴位置
rect.modify(rect.left, rect.top + velocityY,
rect.right, rect.bottom + velocityY);
// 应用重力
velocityY += 1.5f;
// 检查是否落地
if (rect.top >= 0) {
rect.modify(rect.left, 0,
rect.right, rect.bottom - rect.top);
isJumping = false;
velocityY = 0;
}
}
}
// 检查碰撞
public boolean checkCollision(RectFloat obstacle) {
return rect.left < obstacle.right &&
rect.right > obstacle.left &&
rect.top < obstacle.bottom &&
rect.bottom > obstacle.top;
}
// getter方法
public RectFloat getRect() { return rect; }
public boolean isJumping() { return isJumping; }
public boolean isDucking() { return isDucking; }
}
3.2 障碍物类(Obstacle.java)
package com.example.runner;
import ohos.agp.utils.RectFloat;
import java.util.Random;
public class Obstacle {
private RectFloat rect; // 障碍物矩形区域
private int speed; // 移动速度
private boolean isActive; // 是否活动
private int type; // 障碍物类型(0:低障碍,1:高障碍)
public Obstacle(float left, float top, float right, float bottom, int speed, int type) {
this.rect = new RectFloat(left, top, right, bottom);
this.speed = speed;
this.isActive = true;
this.type = type;
}
// 更新位置
public void update() {
rect.modify(rect.left - speed, rect.top,
rect.right - speed, rect.bottom);
// 检查是否超出屏幕
if (rect.right < 0) {
isActive = false;
}
}
// getter方法
public RectFloat getRect() { return rect; }
public boolean isActive() { return isActive; }
public int getType() { return type; }
// 随机生成障碍物
public static Obstacle generateRandom(int screenWidth, int screenHeight, int speed) {
Random random = new Random();
int type = random.nextInt(2); // 0或1
float width = 50;
float height = type == 0 ? 30 : 60;
float top = screenHeight - height;
return new Obstacle(screenWidth, top,
screenWidth + width, screenHeight,
speed, type);
}
}
3.3 游戏视图(GameView.java)
package com.example.runner;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import java.util.ArrayList;
import java.util.Random;
public class GameView extends Component implements Component.DrawTask {
private Player player;
private ArrayList<Obstacle> obstacles = new ArrayList<>();
private int score = 0;
private int speed = 10;
private boolean isGameOver = false;
private Paint scorePaint;
private Paint gameOverPaint;
private PixelMap playerImage;
private PixelMap obstacleImage;
private PixelMap backgroundImage;
private PixelMap groundImage;
private Random random = new Random();
private long lastObstacleTime = 0;
private float groundPosition = 0;
public GameView(Context context) {
super(context);
init();
}
private void init() {
// 初始化画笔
scorePaint = new Paint();
scorePaint.setColor(Color.BLACK);
scorePaint.setTextSize(50);
gameOverPaint = new Paint();
gameOverPaint.setColor(Color.RED);
gameOverPaint.setTextSize(100);
// 初始化玩家
int screenHeight = getHeight();
player = new Player(100, screenHeight - 100, 200, screenHeight);
// 加载图片资源
playerImage = loadPixelMap("entry/resources/base/media/player.png");
obstacleImage = loadPixelMap("entry/resources/base/media/obstacle.png");
backgroundImage = loadPixelMap("entry/resources/base/media/background.png");
groundImage = loadPixelMap("entry/resources/base/media/ground.png");
addDrawTask(this);
setTouchEventListener(this::onTouchEvent);
// 启动游戏循环
startGameLoop();
}
@Override
public void onDraw(Component component, Canvas canvas) {
// 绘制背景
if (backgroundImage != null) {
canvas.drawPixelMapHolder(backgroundImage.createPixelMapHolder(), 0, 0);
} else {
canvas.drawColor(Color.WHITE);
}
// 绘制地面
if (groundImage != null) {
for (int i = 0; i < 3; i++) {
canvas.drawPixelMapHolder(groundImage.createPixelMapHolder(),
groundPosition + i * getWidth(), getHeight() - 50);
}
}
// 绘制玩家
RectFloat playerRect = player.getRect();
if (playerImage != null) {
canvas.drawPixelMapHolder(playerImage.createPixelMapHolder(),
playerRect.left, playerRect.top);
} else {
Paint playerPaint = new Paint();
playerPaint.setColor(Color.BLUE);
canvas.drawRect(playerRect, playerPaint);
}
// 绘制障碍物
for (Obstacle obstacle : obstacles) {
if (obstacle.isActive()) {
RectFloat rect = obstacle.getRect();
if (obstacleImage != null) {
canvas.drawPixelMapHolder(obstacleImage.createPixelMapHolder(),
rect.left, rect.top);
} else {
Paint obstaclePaint = new Paint();
obstaclePaint.setColor(Color.RED);
canvas.drawRect(rect, obstaclePaint);
}
}
}
// 绘制分数
canvas.drawText("分数: " + score, 50, 50, scorePaint);
// 绘制游戏结束
if (isGameOver) {
canvas.drawText("游戏结束!", getWidth() / 2 - 150, getHeight() / 2, gameOverPaint);
}
}
private boolean onTouchEvent(Component component, TouchEvent event) {
if (isGameOver) {
if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
resetGame();
}
return true;
}
switch (event.getAction()) {
case TouchEvent.PRIMARY_POINT_DOWN:
player.jump();
break;
case TouchEvent.POINT_MOVE:
// 长按蹲下
player.duck();
break;
case TouchEvent.PRIMARY_POINT_UP:
player.stand();
break;
}
return true;
}
private void startGameLoop() {
// 使用定时器更新游戏状态
getContext().getUITaskDispatcher().delayDispatch(() -> {
if (!isGameOver) {
long currentTime = System.currentTimeMillis();
// 更新玩家
player.update();
// 生成新障碍物
if (currentTime - lastObstacleTime > 1500 - speed * 10) {
obstacles.add(Obstacle.generateRandom(getWidth(), getHeight(), speed));
lastObstacleTime = currentTime;
}
// 更新障碍物
for (Obstacle obstacle : obstacles) {
obstacle.update();
// 检查碰撞
if (player.checkCollision(obstacle.getRect())) {
isGameOver = true;
}
}
// 更新地面位置
groundPosition -= speed / 2;
if (groundPosition <= -getWidth()) {
groundPosition = 0;
}
// 增加分数
score++;
// 增加难度
if (score % 100 == 0) {
speed++;
}
// 清理不活动的障碍物
cleanupObstacles();
}
invalidate();
// 继续游戏循环
startGameLoop();
}, 16); // 约60帧/秒
}
private void cleanupObstacles() {
for (int i = obstacles.size() - 1; i >= 0; i--) {
if (!obstacles.get(i).isActive()) {
obstacles.remove(i);
}
}
}
private PixelMap loadPixelMap(String path) {
// 实现图片加载逻辑
// 实际项目中应该使用ResourceManager加载资源
return null;
}
public void resetGame() {
obstacles.clear();
player = new Player(100, getHeight() - 100, 200, getHeight());
score = 0;
speed = 10;
isGameOver = false;
lastObstacleTime = System.currentTimeMillis();
groundPosition = 0;
}
}
3.4 主界面(MainAbilitySlice.java)
package com.example.runner;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Text;
import ohos.agp.window.dialog.ToastDialog;
public class MainAbilitySlice extends AbilitySlice {
private GameView gameView;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
DirectionalLayout layout = new DirectionalLayout(this);
layout.setOrientation(DirectionalLayout.VERTICAL);
// 游戏视图
gameView = new GameView(this);
// 按钮布局
DirectionalLayout buttonLayout = new DirectionalLayout(this);
buttonLayout.setOrientation(DirectionalLayout.HORIZONTAL);
buttonLayout.setPadding(10, 10, 10, 10);
// 重新开始按钮
Button resetButton = new Button(this);
resetButton.setText("重新开始");
resetButton.setClickedListener(component -> gameView.resetGame());
// 帮助按钮
Button helpButton = new Button(this);
helpButton.setText("帮助");
helpButton.setClickedListener(component -> showHelp());
buttonLayout.addComponent(resetButton);
buttonLayout.addComponent(helpButton);
layout.addComponent(gameView);
layout.addComponent(buttonLayout);
super.setUIContent(layout);
}
private void showHelp() {
new ToastDialog(this)
.setText("游戏玩法:\n1. 点击屏幕跳跃\n2. 长按屏幕蹲下\n3. 避开障碍物\n4. 坚持越久分数越高")
.show();
}
}
4. 布局文件(ability_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical"
ohos:background_element="#FFFFFF">
<com.example.runner.GameView
ohos:id="$+id:game_view"
ohos:width="match_parent"
ohos:height="0vp"
ohos:weight="1"/>
<DirectionalLayout
ohos:id="$+id:button_layout"
ohos:width="match_parent"
ohos:height="wrap_content"
ohos:orientation="horizontal"
ohos:padding="10vp"
ohos:margin="10vp">
<Button
ohos:id="$+id:reset_button"
ohos:width="0vp"
ohos:height="50vp"
ohos:weight="1"
ohos:text="重新开始"
ohos:text_size="16fp"
ohos:margin="5vp"
ohos:background_element="#4CAF50"/>
<Button
ohos:id="$+id:help_button"
ohos:width="0vp"
ohos:height="50vp"
ohos:weight="1"
ohos:text="帮助"
ohos:text_size="16fp"
ohos:margin="5vp"
ohos:background_element="#2196F3"/>
</DirectionalLayout>
</DirectionalLayout>
5. 游戏核心机制实现
5.1 玩家控制逻辑
// 跳跃
public void jump() {
if (!isJumping) {
velocityY = -25; // 向上的初速度
isJumping = true;
}
}
// 蹲下
public void duck() {
if (!isJumping) {
isDucking = true;
rect.modify(rect.left, rect.top + 20,
rect.right, rect.bottom);
}
}
// 更新位置
public void update() {
if (isJumping) {
// 更新Y轴位置
rect.modify(rect.left, rect.top + velocityY,
rect.right, rect.bottom + velocityY);
// 应用重力
velocityY += 1.5f;
// 检查是否落地
if (rect.top >= 0) {
rect.modify(rect.left, 0,
rect.right, rect.bottom - rect.top);
isJumping = false;
velocityY = 0;
}
}
}
5.2 障碍物生成逻辑
// 随机生成障碍物
public static Obstacle generateRandom(int screenWidth, int screenHeight, int speed) {
Random random = new Random();
int type = random.nextInt(2); // 0或1
float width = 50;
float height = type == 0 ? 30 : 60;
float top = screenHeight - height;
return new Obstacle(screenWidth, top,
screenWidth + width, screenHeight,
speed, type);
}
// 在游戏循环中生成障碍物
if (currentTime - lastObstacleTime > 1500 - speed * 10) {
obstacles.add(Obstacle.generateRandom(getWidth(), getHeight(), speed));
lastObstacleTime = currentTime;
}
5.3 游戏循环与难度递增
private void startGameLoop() {
getContext().getUITaskDispatcher().delayDispatch(() -> {
if (!isGameOver) {
// 更新游戏状态...
// 增加分数
score++;
// 增加难度
if (score % 100 == 0) {
speed++;
}
}
invalidate();
startGameLoop();
}, 16); // 约60帧/秒
}
6. 扩展功能建议
- 多种角色选择:解锁不同外观的角色
- 道具系统:无敌、加速、磁铁等道具
- 场景变化:日夜交替或季节变化
- 音效系统:跳跃音效、碰撞音效等
- 特效系统:粒子效果、动画效果
- 成就系统:记录玩家成就
- 在线排行榜:与全球玩家比拼分数