游戏动图

A. 项目描述

《奔跑吧武士》是一款集动作与策略于一体的Android游戏,旨在为玩家提供一场激动人心的武侠冒险之旅。游戏的功能设计,需确保游戏的流畅体验和高度的娱乐性。

1. 核心玩法: 游戏的核心玩法围绕武士角色的冒险和战斗展开。玩家需要操控武士在各种场景中完成任务,包括击败敌人、收集资源。游戏场景设计需要富有挑战性,包含多样化的敌人类型和障碍物,以测试玩家的策略和反应能力。

2. 战斗与策略: 战斗系统需要平衡动作与策略。玩家可以使用武器和战术来应对敌人,合理运用技能和战术是取胜的关键。战斗中的敌人应具有不同的攻击模式和策略,挑战玩家的应变能力。

3. 图形与音效: 高质量的图形和音效对游戏体验至关重要。游戏应具备精美的武侠风格图形,细致的角色和环境设计,以及动感的音效和背景音乐,以增强沉浸感。

4. 得分成就: 游戏应支持玩家的成绩排行榜和成就系统。这些功能可以激励玩家进行更高水平的挑战,增加玩家的粘性和参与度。

5. 用户界面与体验: 界面设计应简洁直观,确保玩家可以轻松导航并快速上手。游戏操作需要流畅,控制按钮应根据触摸屏进行优化,以提升玩家的操作体验。

这些功能需求将确保《奔跑吧武士》成为一款受玩家喜爱的动作冒险游戏,并为其提供持久的娱乐价值。

B. 开发工具

  • Android Studio Koala
  • Java , JDK 17.0.10
  • Gradle 8.7

C. 代码设计

模块化设计结构: 将游戏代码分成不同的模块,如角色控制、敌人行为、UI管理、音效处理等。每个模块负责游戏的一个特定功能,便于维护和扩展。

数据存储与管理实现: 使用本地存储(SharedPreferences)来保存游戏数据,如游戏等分和成就。

游戏中包含多种角色和对象:主角是武士(Player),还有各种敌人,包括强大的大Boss、巡逻士兵以及具有威胁的乌鸦。此外,游戏还设有食物(如苹果)和武器等道具。 游戏的主要显示由 DashSurfaceView 处理,它继承自 SurfaceView,负责游戏界面的渲染和用户操作的响应。通过 DashSurfaceView,游戏能够实现平滑的图形绘制和实时的用户输入处理,确保流畅的游戏体验。

DashSurfaceView 中,执行了游戏线程,代码如下:

public void run() {
		while (flag) {
			long start = System.currentTimeMillis();
			if (pageResume) {
				myDraw();// 绘制游戏画面
				logic();// 执行游戏逻辑
			}
			long end = System.currentTimeMillis();
			try {
				if (end - start <= 100)
					Thread.sleep(100 - end + start);
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
	}

实现了定时刷新游戏画面、执行游戏逻辑。

绘制游戏画面定义在 myDraw函数中,通过Canvas绘制游戏画面,同时针对不通的绘制对象进行了代码封装,便于维护和扩展,代码如下:

	private void myDraw() {
		try {
			canvas = holder.lockCanvas();
			if (canvas != null) {
				switch (status) {
					// 绘制loading界面
					case Tools.GAME_LOADING:
						loading.draw(canvas, paint);
						break;
					// 绘制 GAME_MENU 界面
					case Tools.GAME_MENU:
						gameMenu.draw(canvas, paint);
						break;
					// 绘制GAME_PLAYING 界面
					case Tools.GAME_PLAYING:
						gameing_bg.draw(canvas, paint);
						for (int i = 0; i < veEnemy.size(); i++) {
							veEnemy.elementAt(i).onDraw(canvas, paint);

						}
						mapDraw();
						mybutton.draw(canvas, paint);
						player.draw(canvas, paint);

						// 子弹
						for (int i = 0; i < vetorBullet.size(); i++) {
							vetorBullet.elementAt(i).onDraw(canvas, paint);
						}
						// 死亡效果
						for (int i = 0; i < veDead.size(); i++) {
							veDead.elementAt(i).onDraw(canvas, paint);

						}
						// 食物
						for (int i = 0; i < vcFood.size(); i++) {
							vcFood.elementAt(i).onDraw(canvas, paint);
						}
						// 绘制Boss
						if (boss != null) {
							if (boss.isDead() == false) {
								boss.draw(canvas, paint);
							}
						}
						// boss子弹
						if (boss != null) {
							for (int i = 0; i < vetorBulletBoss.size(); i++) {
								vetorBulletBoss.elementAt(i).onDraw(canvas, paint);

							}
						}
						break;
					// 绘制GAME_OVER 界面
					case Tools.GAME_OVER:
						// 将最后的米数给gameover界面
						Game_Over.latestmeter = gameing_bg.getMeter();
						gameover.draw(canvas, paint);
						break;
					// 绘制 GAME_PAUSE 界面
					case Tools.GAME_PAUSE:
						gameing_bg.draw(canvas, paint);
						mapDraw();
						pause.draw(canvas, paint);
						break;
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			if (canvas != null && null!=holder) {
				if (holder.getSurface().isValid()) {
					holder.unlockCanvasAndPost(canvas);
				}
			}
		}

	}

游戏的主要逻辑定义在 logic 函数中,针对不通的场景、功能界面分别做了明确定义,同时对相应的代码进行了封装,代码实现如下:

	public void logic() {
		switch (status) {
			// loading界面逻辑
			case Tools.GAME_LOADING:
				if (isFirstLoadLoading) {
					initImage(); // 初始化图片
					isFirstLoadLoading = false;
				}
				init(); // 初始化游戏
				status = Tools.GAME_PLAYING;

				if (countMusic++ == 0) {
					GameMusic.startMusic();// 播放 GAME_PLAYING 界面 背景音乐;
					GameMusic.playSound(R.raw.land, 0);// 播放一次落地 音效
				} else {
					GameMusic.nextMusic(R.raw.bg);
					GameMusic.playSound(R.raw.land, 0);// 播放一次落地 音效
				}

				break;
			// GAME_MENU 界面 逻辑
			case Tools.GAME_MENU:

				gameMenu.logic();

				break;
			// GAME_PLAYING 界面逻辑
			case Tools.GAME_PLAYING:
				gameing_bg.logic();
					// ……
				
				// 设置地图速度
				for (int i = 0; i < vcMap.size(); i++) {
					// ……
				}
				// 设置敌人速度
				for (int i = 0; i < veEnemy.size(); i++) {
					// ……
					// 设置食物速度
					for (int j = 0; j < vcFood.size(); j++) {
					// ……
					}
				}
				//设置子弹偏移量
				for (int i = 0; i < vetorBullet.size(); i++) {
					// ……
				}
				for (int i = 0; i < vetorBulletBoss.size(); i++) {
					// ……
				}
				// // ////////
				// 主角的逻辑处理-----------------------------------------------------------------------------------------------------
				player.logic();
				// 判断主角与木桩的碰撞
				for (GameMap e : vcMap) {

					// 判断主角是否在地上的桥之上
					// ……
					// 判断主角是否在天上的桥之下
					// ……
					// 判断主角与左突起的碰撞
					// ……

					// 判断主角与桥桩子的碰撞
					// ……
					
					// 如果屏幕停止了,但是忍者跳到了木桩之上,则要让屏幕继续移动
					// ……
				}


				if (player.getIsPlayerDead()) {
					// 播放死亡声效
					// 暂停游戏中背景音乐
					// ……
					status = Tools.GAME_OVER;
				}

				// 主角的逻辑处理--------------------------------------------------------------------------------------
				// 人物子弹
				if (player.getIsShootMore()) {
					// ……
				}
				// 人物子弹移除方法及逻辑及打到boss
				for (int i = 0; i < vetorBullet.size(); i++) {
					// ……
				}
				// 乌鸦
				if (gameing_bg.getMeter() % 100 == 0) {
					Enemy enemy = new Enemy(Tools.ENEMY_CROW, bmpCrow);
					veEnemy.add(enemy);
					enemy = null;
				}
				for (int i = 0; i < veEnemy.size(); i++) {
					Enemy c = veEnemy.elementAt(i);
					// ……
				}
				// 碰撞
				for (int j = 0; j < veEnemy.size(); j++) {
					// 子弹
					for (int i = 0; i < vetorBullet.size(); i++) {
					// ……
						}
					}
					// 人物无敌与敌人
					if (player.getIsUndead()) {
					// ……
					}
				}
				// 死亡效果
				for (int i = 0; i < veDead.size(); i++) {
					// ……
				}

				// 食物
				if (gameing_bg.getMeter() %250 == 0) {
					// ……
				}
				if (gameing_bg.getMeter() % 150 == 0) {
					// ……
				}
				for (int i = 0; i < vcFood.size(); i++) {
					// ……
				}
				// 敌人与主角碰撞
				for (Enemy e : veEnemy) {
					// ……
				}
				// 食物与主角碰撞
				for (Food f : vcFood) {
					// ……
				}
				// Boss逻辑
				if (gameing_bg.getMeter() % 1000 == 0) {
					boss = new Boss(vcboss);
				}
				if (boss != null) {
					boss.logic();
					// ……
					}

				}
				// boss子弹与主角碰撞
				for (int i = 0; i < vetorBulletBoss.size(); i++) {
					// ……
				}
				break;
			// GAME_OVER 界面逻辑
			case Tools.GAME_OVER:
				gameover.logic();
				break;
			// GAME_PAUSE 界面逻辑
			case Tools.GAME_PAUSE:
				break;
		}
	}

触屏事件定义在 DashSurfaceView 类的 onTouchEvent函数中,分别处理了在菜单页面、游戏进行时页面、游戏暂停页面以及游戏结束页面的触控事件,代码如下:

	public boolean onTouchEvent(MotionEvent event) {
		switch (status) {
			case Tools.GAME_MENU:
				gameMenu.onTouchEvent(event);// GameMenu 界面 触屏事件
				break;
			case Tools.GAME_PLAYING:
				if (!mybutton.onTouchEvent(event) && !player.getIsJumpTwice()
						&& !player.getIsScreenDown()) {
					// Log.e("",
					// !mybutton.onTouchEvent(event)+","+!player.getIsJumpTwice()+","+!player.getIsDrawRope()+"");
					gameing_bg.onTouchEvent(event, player.getPosition()[1]);
				}
				// 将当前的米数传给暂停界面用于显示
				Pause.currentmeter = gameing_bg.getMeter();
				mybutton.onTouchEvent(event);// 暂停,,,发表按钮触屏事件
				player.ontouch(event, mybutton.getPosition(),
						mybutton.getPositionLeftRound());
				// //////33333开火
				if (inRect((int) event.getX(), (int) event.getY(), rect)) {
					if (event.getAction() == MotionEvent.ACTION_DOWN) {
						bulletFire = true;
					} else {
						bulletFire = false;
					}
					if (event.getAction() == MotionEvent.ACTION_UP) {
						bulletFire = false;
					}
				}
				break;
			case Tools.GAME_OVER:
				gameover.onTouchEvent(event);
				break;
			case Tools.GAME_PAUSE:
				pause.onTouchEvent(event);// GamePause 界面 触屏事件
				break;
		}
		return true;
	}

D. 项目演示

游戏动图

UI

E. 项目源码

项目代码截图

关注公众号『数字森林』,后台发送关键字:<mark>武士</mark>,获取项目源码。