鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
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或1float 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();}@Overridepublic 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;@Overridepublic 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"?>
<DirectionalLayoutxmlns: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.GameViewohos:id="$+id:game_view"ohos:width="match_parent"ohos:height="0vp"ohos:weight="1"/><DirectionalLayoutohos:id="$+id:button_layout"ohos:width="match_parent"ohos:height="wrap_content"ohos:orientation="horizontal"ohos:padding="10vp"ohos:margin="10vp"><Buttonohos: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"/><Buttonohos: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或1float 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. 扩展功能建议
- 多种角色选择:解锁不同外观的角色
- 道具系统:无敌、加速、磁铁等道具
- 场景变化:日夜交替或季节变化
- 音效系统:跳跃音效、碰撞音效等
- 特效系统:粒子效果、动画效果
- 成就系统:记录玩家成就
- 在线排行榜:与全球玩家比拼分数
相关文章:
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...