鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

news2025/6/12 17:59:24

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. 扩展功能建议

  1. ​多种角色选择​​:解锁不同外观的角色
  2. ​道具系统​​:无敌、加速、磁铁等道具
  3. ​场景变化​​:日夜交替或季节变化
  4. ​音效系统​​:跳跃音效、碰撞音效等
  5. ​特效系统​​:粒子效果、动画效果
  6. ​成就系统​​:记录玩家成就
  7. ​在线排行榜​​:与全球玩家比拼分数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407418.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…

初学 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用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…

Springboot社区养老保险系统小程序

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

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

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

ABAP设计模式之---“简单设计原则(Simple Design)”

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

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…

USB Over IP专用硬件的5个特点

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

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格

听写流程自动化实践,轻量级教育辅助

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

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…