概述

游戏是简单2D横版闯关游戏。只包括金币,敌人。

主要组件有:

  • GameController(游戏全局配置)
  • TokenController(金币统一控制)
  • MetaGameController(打开关闭菜单,暂停开始游戏)
  • MainUIController(菜单界面)
  • PlayerController(玩家控制)

GameController

单例模式实现全局访问,属性 model : PlatformerModel 存储全局组件和参数配置。

namespace Platformer.Mechanics
{/// <summary>/// This class exposes the the game model in the inspector, and ticks the/// simulation./// </summary> public class GameController : MonoBehaviour{public static GameController Instance { get; private set; }//This model field is public and can be therefore be modified in the //inspector.//The reference actually comes from the InstanceRegister, and is shared//through the simulation and events. Unity will deserialize over this//shared reference when the scene loads, allowing the model to be//conveniently configured inside the inspector.public PlatformerModel model = Simulation.GetModel<PlatformerModel>();void OnEnable(){Instance = this;}void OnDisable(){if (Instance == this) Instance = null;}void Update(){if (Instance == this) Simulation.Tick();}}
}

TokenController

统一控制全部的金币

// 添加组件菜单项
[ContextMenu("Find All Tokens")]
void FindAllTokensInScene()
{tokens = UnityEngine.Object.FindObjectsOfType<TokenInstance>();
}


*这里在Update里面统一控制金币动画

 void Update(){//if it's time for the next frame...if (Time.time - nextFrameTime > (1f / frameRate)){//update all tokens with the next animation frame.for (var i = 0; i < tokens.Length; i++){var token = tokens[i];//if token is null, it has been disabled and is no longer animated.if (token != null){token._renderer.sprite = token.sprites[token.frame];if (token.collected && token.frame == token.sprites.Length - 1){token.gameObject.SetActive(false);tokens[i] = null;}else{token.frame = (token.frame + 1) % token.sprites.Length;}}}//calculate the time of the next frame.nextFrameTime += 1f / frameRate;}}

MetaGameController

 void Update(){if (Input.GetButtonDown("Menu")){ToggleMainMenu(show: !showMainCanvas);}}
// 控制菜单界面打开关闭

*Time.timeScale = 0 实现游戏暂停,Time.timeScale 影响 FixedUpdate 和其他 Time 中的相关值。

背景移动

游戏中不同背景的移动速度是不一样的。

实现方法是通过 ParallaxLayer.cs脚本控制,核心代码如下:

void LateUpdate()
{// 通过设置一个移动缩放比例,使背景移动距离是相机的一定比例// 远景山等比例为0.5,近景建筑比例为0.25,远景比近景移动要快,模拟透视transform.position = Vector3.Scale(_camera.position, movementScale);
}

相机跟随

相机使用的是 Cinemachine

设置跟随角色,并设置限制范围。

黄线即为相机设置的限制范围。

地图绘制

地图绘制使用 Tilemap(核心组件:Tilemap,Tilemap Renderer,Tilemap Collider 2D)
另外可以结合 Composite Collider 2D

隐藏通道

隐藏通道可以通过新建一个图层绘制Tilemap,不添加Collider来实现。

死亡胜利

在场景下方和其他限制区域绘制碰撞盒子,作为Trigger,通过OnTriggerEnter2D触发死亡或者胜利事件。

namespace Platformer.Mechanics
{/// <summary>/// DeathZone components mark a collider which will schedule a/// PlayerEnteredDeathZone event when the player enters the trigger./// </summary>public class DeathZone : MonoBehaviour{void OnTriggerEnter2D(Collider2D collider){var p = collider.gameObject.GetComponent<PlayerController>();if (p != null){var ev = Schedule<PlayerEnteredDeathZone>();ev.deathzone = this;}}}/// <summary>/// Marks a trigger as a VictoryZone, usually used to end the current game level./// </summary>public class VictoryZone : MonoBehaviour{void OnTriggerEnter2D(Collider2D collider){var p = collider.gameObject.GetComponent<PlayerController>();if (p != null){var ev = Schedule<PlayerEnteredVictoryZone>();ev.victoryZone = this;}}}
}

怪物移动

怪物的移动是通过组件 PatrolPath 绘制移动路径

            /// <summary>/// Get the position of the mover for the current frame./// </summary>/// <value></value>public Vector2 Position{get{p = Mathf.InverseLerp(0, duration, Mathf.PingPong(Time.time - startTime, duration));return path.transform.TransformPoint(Vector2.Lerp(path.startPosition, path.endPosition, p));}}

角色物理表现

角色的移动,跳跃碰撞都是通过 KinematicObject 实现对物理属性的实现。
主要在 PerformMovement 中实现物理计算。

protected virtual void FixedUpdate(){//if already falling, fall faster than the jump speed, otherwise use normal gravity.if (velocity.y < 0)velocity += gravityModifier * Physics2D.gravity * Time.deltaTime;elsevelocity += Physics2D.gravity * Time.deltaTime;velocity.x = targetVelocity.x;IsGrounded = false;var deltaPosition = velocity * Time.deltaTime;// 取地面法线的垂直向量,即沿着地面移动的方向var moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);var move = moveAlongGround * deltaPosition.x;PerformMovement(move, false);move = Vector2.up * deltaPosition.y;PerformMovement(move, true);}void PerformMovement(Vector2 move, bool yMovement){var distance = move.magnitude;if (distance > minMoveDistance){//check if we hit anything in current direction of travelvar count = body.Cast(move, contactFilter, hitBuffer, distance + shellRadius);for (var i = 0; i < count; i++){var currentNormal = hitBuffer[i].normal;//is this surface flat enough to land on?// 判断法线向量的 y 是否能够站立,可以据此实现攀墙等功能if (currentNormal.y > minGroundNormalY){IsGrounded = true;// if moving up, change the groundNormal to new surface normal.if (yMovement){groundNormal = currentNormal;currentNormal.x = 0;}}if (IsGrounded){//how much of our velocity aligns with surface normal?// 根据点乘判断移动方向和地面方向,实现爬坡阻力var projection = Vector2.Dot(velocity, currentNormal);if (projection < 0){//slower velocity if moving against the normal (up a hill).velocity = velocity - projection * currentNormal;}}else{//We are airborne, but hit something, so cancel vertical up and horizontal velocity.// 在空中碰到物体velocity.x *= 0;velocity.y = Mathf.Min(velocity.y, 0);}//remove shellDistance from actual move distance.// 修正移动距离var modifiedDistance = hitBuffer[i].distance - shellRadius;distance = modifiedDistance < distance ? modifiedDistance : distance;}}body.position = body.position + move.normalized * distance;}

结尾

小游戏的大部分流程是在 Gameplay 包下模拟的,包括响应事件,对动画状态机的控制。

Unity Learn 项目 2D Platformer Microgame 教程(代码分析-功能实现方法)相关推荐

  1. Unity新项目如何快速理清顶层代码结构

    Unity新项目如何快速理清顶层代码结构 半路上手新项目时弄懂scene切换流程是有必要的,scene切换可以说是unity最上层的代码资源结构 思路 首先我们在入口scene(第一个scene)中添 ...

  2. 使用VS2010代码分析功能增强ASP.NET应“.NET研究”用程序安全

    任何从事ASP.NET开发的人都不得不承认,在其职业生涯中曾经遇到过应用程序安全问题,开发人员常常被迫尽快交付代码,平台的复杂性和各种配置选项让应用程序的安全总达不到预期,此外,调试和生产环境的配置要 ...

  3. 扫描功能代码编写_干净的代码编写功能或方法

    扫描功能代码编写 This article is the second of a clean code series, you can find the first part about Clean ...

  4. Unity开源项目2D流体渲染实现分析

    开源项目LiquidEffect渲染分析 项目传送门 实现流程 步骤一 创建刚体 步骤二 降采样与模糊处理 降采样分析 C#部分 顶点着色器 片元着色器 分析 模糊分析 步骤三 Alpha过滤 项目传 ...

  5. RPG 游戏 unity ngui 实现2D名字牌血条等功能

    名字牌包括2D,3D名字牌两种,区别在于2D是基于屏幕坐标的名字牌,将名字牌的gameobject结点挂在UI相机下,不会受3D场景中物件的影响,3D名字牌可以视为游戏场景内的一部分物件,名字牌跟随角 ...

  6. vue2项目使用codemirror插件实现代码编辑器功能

    1.使用npm安装依赖 npm install --save codemirror 2.在页面中放入如下代码 <template><textarea ref="mycode ...

  7. Notepad++强大的代码补全和代码提示功能的方法

    最近写项目,经常要打开一些文件去修改一些代码段.那么我的项目都是使用ied大型编辑器去写的,每次修改文件,哪怕是一个标点都要用一分钟时间去打开软件.当然,后来我也考虑到使用记事本,但总感觉不是很爽. ...

  8. oxooooooof4蓝屏_常见电脑蓝屏代码分析与解决方法解析大全

    每次电脑出现蓝屏,你是不是很头痛额? 自己看到那些像天文般的英文,是不是眼花缭乱? 呵呵,小编我今天汇总额一些常见的30个蓝屏代码,为您排忧解难!! 停止错误编号:OXOOOOOOOA 说明文字:IR ...

  9. unity脚本用vs打开没有代码提示功能

    问题:当我们在unity启动c#脚本时,即使安装了Visual Studio 2010 Tools for Unity,在vs中开启了代码提示功能也无法提示. 解决方法: 1.在unity的Edit- ...

最新文章

  1. R语言使用aov函数进行单因素协方差分析(One-way ANCOVA)、单因素协方差分析(ANCOVA)扩展了单因素方差分析,包括一个或多个协变量(covariates)
  2. 多视图几何总结——基础矩阵、本质矩阵和单应矩阵的求解过程
  3. 大数开方(C++版)
  4. 网易严选Java开发三面面经:mysql索引面试题
  5. 【推荐实践】深度学习在阿里B2B电商推荐系统中的实践
  6. python实现电脑程序自动化_python基于pywinauto实现PC客户端自动化
  7. 面向对象——抽象基类
  8. boost asio异步通信
  9. PHP魔术方法和魔术变量总结
  10. nginx php转发_nginx做前端转发,将php交给php-fpm处理
  11. DXperience 12.2使用手册
  12. 永凯APS生产排程软件同时考虑物料及产能
  13. C语言4位BCD码加法器,四位二进制8421BCD码加法器
  14. 阿里云安全防护是如何做到的?有怎样的安全生态圈
  15. Python程序员必备——手把手教你配置最漂亮的PyCharm界面
  16. 关于python无法显示中文的问题:SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3
  17. AWT绘图工具Graphics
  18. LogMeIn软件介绍
  19. 奥运排行榜(25 分)
  20. 【国际化】vue2+uniapp实现国际化

热门文章

  1. import pyyaml Error
  2. 手把手教你使用HFSS仿真高速差分过孔—上
  3. 联发科推出天玑1200,新一年的头道“5G甜点”究竟滋味如何?
  4. 混合开发入门,java jdk和android sdk安装配置+模拟器安装配置+android studio运行起项目--windows电脑
  5. 智能型低压电网无功补偿器的设计
  6. aubo机械臂控制方式
  7. 微信公众号、人脉拓展、运营
  8. 如何用php做每天日程安排,PHP开发制作一个简单的活动日程表Calendar,日程表calendar...
  9. 哪里可以找到超清实时的谷歌卫星地图?
  10. 发售GIGABYTE制的Atom搭载Convertible UMPC「M912X」,价格79,800日元