【摘要】这同样是sunset在学习Unity游戏制作过程中独立制作的游戏,游戏的操作与过关方式比较简单,整体制作时间大概两到三天中除去上课吃饭以及睡觉的剩余时间。虽然也出现了意想不到的小问题,不过整体制作过程中还算比较顺利。这篇博客就来说说如何制作一款这样的游戏的核心部分。

1.游戏背景简介

这款游戏的故事背景大概讲的是一群不知为何从地底复活的古生物——萌萌的小骷髅为了实现蠢蠢的想法而想要占领城市,它们各种破坏城市,制作了各种妨碍人们正常生活的事,此时一位热衷于跑步的少年出现,他的出现显然不是为了拯救城市,而只是一心一意的专注于奔跑,然而在这个过程却又要免于小骷髅的骚扰,故事就从这里开始了。。。

2.我们所需要的

制作任何一款游戏都离不开合适于游戏的人物模型,所以我们先需要一下这些东西:

  1. 适合游戏故事背景的萌萌的主人公,也就是玩家控制的角色模型。
  2. 适合游戏故事背景的萌萌的小骷髅,也就是逻辑意义上的敌人。
  3. 适合游戏故事背景的场景模型以及障碍物模型,因为游戏发生在现代都市,所以可以是车辆,树木,建筑物等等。
  4. 合适的音频文件
  5. 一颗耐心,静静去完成属于自己的独立作品。

3.编写人物移动代码

在此类游戏中,最重要的就是玩家操作感受,于是优先考虑移动代码的编写是非常需要的,虽然这个游戏的过程是2D游戏,但是sunset在创建场景等一切模型元素上采用的都是3D模型,所以游戏整体上在视角方面更有层次感。因为我们这是2D游戏,并不会在平面上突然出现一个斜坡,所以人物的移动只需要使用this.transform.Translate()即可,但是人物身上还是需要载有CharacterController组件以用于进行角色跳跃。然而这都不是最难的,因为角色控制器的存在,无法使用刚体的一切属性,所以就不得不对游戏场景的重力进行模拟,只有一遍一遍不断的尝试才能找到合适感觉。接下来是代码:

using UnityEngine;
using System.Collections;[RequireComponent(typeof(Animator))]
public class RunnerController : MonoBehaviour
{//public Variablepublic enum Direction{Forward = 90,Backward = 270,}public float maxSpeed;public float MoveSpeed = 0;public float Z;public float JumpForce;public float Gravity;public bool PlayerDead;//private variableprivate CharacterController Controller;private Animator animator;private Direction direction = Direction.Forward;private float H;private float V;private bool OnGround = true;private bool Jump;private float JSpeed;void Awake(){//rigidbody = this.GetComponent<Rigidbody>();Controller = this.GetComponent<CharacterController>();animator = this.GetComponent<Animator>();}void Update () {if(!PlayerDead){if(this.transform.position.z != Z){Vector3 Position = this.transform.position;Position.z = Z;this.transform.position = Position;}H = Input.GetAxis("Horizontal");V = Input.GetAxis("Vertical");//AniamtorState();if(Controller.isGrounded){OnGround = true;Jump = false;animator.SetBool("Jump",false);if(Input.GetKeyDown(KeyCode.J)){if(V > 0.3){JSpeed = 1.3f * JumpForce;}else{JSpeed = JumpForce;}Jump = true;animator.SetBool("Jump", true);}}else{OnGround = false;if(!OnGround){Controller.Move(-Vector3.up * Gravity * Time.deltaTime);}}}else{animator.SetBool("Dead",true);animator.SetFloat("speed", 0);animator.SetFloat("Slider", 0);animator.SetBool("Jump", false);}}void FixedUpdate(){JumpUp();Move();}void Move(){//先计算朝向if(H >= 0.3){if(MoveSpeed == 0){SetFacingDirection(Direction.Forward);}if(direction == Direction.Forward){if(MoveSpeed < maxSpeed){MoveSpeed += 1.0f;//state = State.Walk;}else{MoveSpeed = maxSpeed;//state = State.Run;}}}else if(H <= -0.3){if(MoveSpeed == 0){SetFacingDirection(Direction.Backward);}if(direction == Direction.Backward){if(MoveSpeed < maxSpeed){MoveSpeed += 1.0f;//state = State.Walk;}else{MoveSpeed = maxSpeed;//state = State.Run;}}}if((direction == Direction.Forward && H < -0.3 && MoveSpeed != 0)|| (direction == Direction.Backward && H > 0.3 && MoveSpeed != 0)){MoveSpeed -= 1.0f;if(MoveSpeed <= 0){MoveSpeed = 0;//state = State.Idle;}}if(Mathf.Abs(H) < 0.3 && MoveSpeed != 0){MoveSpeed -= 0.5f;if(MoveSpeed <= 0){MoveSpeed = 0;//state = State.Run;}}transform.Translate(Vector3.forward * MoveSpeed * Time.deltaTime);animator.SetFloat("Speed", MoveSpeed);animator.SetFloat("Slider", V);}void JumpUp(){if(Jump){JSpeed -= 2 * Gravity * Time.deltaTime;Controller.Move(Vector3.up * Time.deltaTime * JSpeed);}}void SetFacingDirection(Direction dir){if(direction != dir){transform.Rotate(Vector3.up * (direction - dir));direction = dir;}}void OnTriggerEnter(Collider _collider){if(_collider.gameObject.tag == "Enemy"){PlayerDead = true;}}
}

同时,sunset在这个脚本中使用代码模拟了人物移动惯性,也就是说,人物跑动过程中并不会因为玩家松开了移动的按钮而停止向前运动,而会再向前运动一段距离知道当前速度为0后,才可转向或不转向进行新的移动。当按下Jump按钮后,人物会向上跳跃,在跳跃离开地面的时刻起初始跳跃速度(向量:既有方向又有大小)就会因为受到重力的影响而不断减小甚至改变方向直到落地后归为0;同时如果在跳跃之前按下向上键+Jump键,则能跳的更高。jump是利用角色控制器组件的功能进行实现的,如果有所疑问就查API,有详细解释以及示例,真真实实的。人物的AnimatorController是这样的:
这里的walk状态和Run状态之间的切换是通过角色当前的移动速度进行判定的,其实这里使用混合树(blendTree)是更好的,但是sunset在当时制作时没有考虑到,之后事情很多也就没心情再去改了,提及一下。

4)怪物AI代码

2D游戏的怪物AI相较于3D游戏会简单许多,我们只需要让敌人在两点之间循环进行巡逻,并在目标点进行短暂的休息或者说嘲讽动作。所以我们需要在敌人可移动到的两个点上创建连个空物体,分别指示给需要在两点之间巡逻的敌人即可。移动方面仍然使用transform.Translate()方法。
代码如下:

using UnityEngine;
using System.Collections;public class SKT_AI : MonoBehaviour
{public enum State{Idle,Walk,Run,Death,Eat,}public Transform[] _MovePoints = new Transform[2];public float _WaitTimer;public float _WalkSpeed;public float _RunSpeed;[HideInInspector]public bool _Dead;//private Variableprivate State _state;private State _Laststate = State.Idle;private GameObject _Player;private Vector3 _TargetPosition;private int _PointIndex;private Animator _animator;private float _Speed;private bool _playerDead = false;private float _WaitTime;void Awake(){_Player = GameObject.FindGameObjectWithTag("Player");_animator = this.GetComponent<Animator>();}void Start(){_TargetPosition = _MovePoints[0].position;}void FixedUpdate(){if(_state != State.Death){if(_Dead){_state = State.Death;}else{if((_Player.transform.position.x > _MovePoints[0].position.x) && (_Player.transform.position.x < _MovePoints[1].position.x)){Chase();}else{MoveAround();}}}}void MoveAround(){float _Distance = Vector3.Distance(_TargetPosition, this.transform.position);if(_Distance > 1.0f){_state = State.Walk;this.transform.LookAt(_TargetPosition);this.transform.Translate(Vector3.forward * _Speed * Time.deltaTime);}else{if(_Laststate == State.Run){_TargetPosition = new Vector3(_MovePoints[0].position.x, this.transform.position.y, this.transform.position.z);_Laststate = State.Idle;}_state = State.Idle;_WaitTime -=Time.deltaTime;if(_WaitTime <= 0.0f){if(_TargetPosition.x == _MovePoints[0].position.x){_TargetPosition = _MovePoints[1].position;}else if(_TargetPosition.x == _MovePoints[1].position.x){_TargetPosition = _MovePoints[0].position;}_WaitTime = _WaitTimer;}}JudgeState();}void Chase(){if(!_playerDead){_state = State.Run;_TargetPosition = new Vector3(_Player.transform.position.x, this.transform.position.y, this.transform.position.z);this.transform.LookAt(_TargetPosition);this.transform.Translate(Vector3.forward * _Speed * Time.deltaTime);}else{_state = State.Eat;}_Laststate = State.Run;JudgeState();}void JudgeState(){if(_state == State.Idle){_animator.SetBool("Run", false);_animator.SetBool("Walk", false);_animator.SetBool("Eat", false);_Speed = 0.0f;}else if(_state == State.Walk){_animator.SetBool("Run", false);_animator.SetBool("Eat", false);_animator.SetBool("Walk", true);_Speed = _WalkSpeed;}else if(_state == State.Run){_animator.SetBool("Run", true);_animator.SetBool("Eat", false);_animator.SetBool("Walk", false);_Speed = _RunSpeed;}else if(_state == State.Eat){_animator.SetBool("Eat", true);_animator.SetBool("Run", false);_animator.SetBool("Walk", false);_Speed = 0.0f;}else if(_state == State.Death){_animator.SetBool("Dead", true);_animator.SetBool("Run", false);_animator.SetBool("Walk", false);_animator.SetBool("Eat", false);_Speed = 0.0f;Destroy(this.gameObject, 3);}}
}

这里sunset采用的是当玩家移动到敌人巡逻的两点之间的时候,即玩家人物的X坐标大小处于两点的X坐标大小之间,就让敌人进行Chase状态,对玩家进行追踪,如果碰到玩家,则玩家死亡,敌人开始Eat的动作,是不是萌萌的,哈哈。当移动到巡逻的两个边界点上时就进行嘲讽(Taunt)状态,并在一定时间后继续朝另一个边界点进行巡逻运动。下面是敌人的状态机:

5)Camera的设置

在2D游戏中当角色移动的时候,Camera需要跟随角色进行移动,但也不是不论角色移动怎样的距离都进行移动,所以需要一个X轴上的Margin值来限定Camera不移动的人物移动最大距离。代码:

    public float XMargin;public float YMargin;public float XSmooth = 8.0f;public float YSmooth = 8.0f;public Vector3 MaxXAndY;public Vector3 MinXAndY;public Transform Target;bool CheckXMargin(){return  Mathf.Abs(transform.position.x - Target.position.x) > XMargin;}bool CheckYMargin(){return  Mathf.Abs(transform.position.y - Target.position.y) < YMargin + 89.0f;}void FixedUpdate(){FollowTarget();}void FollowTarget(){float targetX = transform.position.x;float targetY = transform.position.y;if(CheckXMargin()){targetX = Mathf.Lerp(transform.position.x, Target.position.x, XSmooth * Time.deltaTime);}if(CheckYMargin()){targetY = Mathf.Lerp(transform.position.y, Target.position.y + targetY, YSmooth * Time.deltaTime);}transform.position = new Vector3(targetX, transform.position.y, transform.position.z);}
}

XMargin和YMargin值可以在Inspector视图中进行自由的修改。而Camera的跟随主要是利用 Mathf.Lerp()方法进行实现的。XSmooth和YSmooth主要用于影响Camera移动过程中的平滑程度。
嗯,这款游戏的核心部分主要就是这些了,如果还有其他自己想要实现的部分,可以再细细琢磨,慢慢修改。
接下来,上试玩图片:

补充:源码及资源下载地址:http://pan.baidu.com/s/14R1me

【Unity】项目源码——2D横版过关类游戏《A_Standard_Runner》相关推荐

  1. Unity 2D横版通关 小游戏——幻城探险 C#

    Unity 2D横版通关 小游戏 C#  分辨率 1920*1980 点击这里下载项目工程 本项目,为个人练习demo,美术资源原创自合作美工. 本项目涉及到的功能: 1.UI:开始游戏按钮,游戏帮助 ...

  2. 手机横版动作类游戏的开发思路

    转自:当乐网 原文:http://www.d.cn/news/289.html 说起横版动作类游戏,对我们这代人影响最深的作品,应该是日本80.90年代出品的一批街机游戏,像<双截龙>.& ...

  3. unity项目源码_在Unity中使用protobuf

    Protocol Buffers (通常简称为protobuf) 是Google开发的一种格式,这种格式与开发语言无关.与运行平台无关,用于序列化结构数据,并且很容易扩展.这种格式可以用于通信协议.数 ...

  4. 用Unity开发一款2D横版游戏demo

    # LanW Game Project 目录 (一) 介绍 (二) 安装教程 (三) 开发流程 1.  新建工程 2.  设置人物 3.  控制主角的移动 4.   添加切换动作的动画 5.   镜头 ...

  5. java手机小游戏源码_Java手机版数独小游戏(J2me)JAVA游戏源码下载

    数独游戏,相信朋友们都知道的,以前也经常玩的,用VB.VC++和Delphi版编写的都在网上宣布过,今天放出一个鉴于Java的J2me手机版的,大致看一下截图,这是在Java模拟机运行的界面,带有Ja ...

  6. U3D学习项目一:2D横版小狐狸闯关游戏

    一.版权出处 该学习内容均来自B站up主:M_Studio 的教学视频,初衷是为了以后忘了某些流程方便检索,非商用!!!! 二.Cinemachine 1.通过Windows--Packages Ma ...

  7. 2D横版摩托游戏源码

    这是一款2D横版摩托特技游戏.代码结构和传统写法有些差异,但是效果还算不错. 传统的写法可能会使用box2d引擎,或者至少模拟出一个简单的物理模型,这里代码有点另类,甚至连速度变量都没有,竟然也能做出 ...

  8. Unity3D横版过关游戏(一)

    想用Unity3D做一个横版过关的游戏,类似Mario的. 发现国外有一个很好的教程网站: http://walkerboystudio.com/html/unity_training___free_ ...

  9. 微信小程序宠物商城项目源码来了~

    微信小程序蒙服汇项目源码来了~ 微信小程序垃圾分类项目源码来了~ 微信小程序校园社团管理系统项目源码来了~ 团队承接各类小程序定制,需要加微 code_gg_boy .小商城,购物,公司的一些管理,流 ...

  10. 开发框架——横版格斗——动作游戏教程

    动作游戏教程 游戏类型: 游戏Demo属于横版格斗游戏,是动作游戏的一种.动作游戏(Action Game),简称ACT,游戏类型的一种,是广义上的游戏类型.以"动作"作为游戏主要 ...

最新文章

  1. Struts编程心得
  2. 快速幂模板(java)
  3. 计算机网络基础与信息安全,大学计算机基础计算机网络基础与信息安全计算机网络概述教案.doc...
  4. java中Logger.getLogger(Test.class)
  5. 11_拼接与拆分,cat,stack,split,chunk
  6. [改善Java代码]不要覆写静态方法
  7. java同时关闭两个窗口_在一个窗口中同时关闭多个窗口的问题(Swing中事件多点传送的问题) | 学步园...
  8. Git成立14周年快乐:您对Git钟爱什么?
  9. dpdk的无锁环形队列Ring
  10. Spring Security 学习-环境搭建(一)
  11. ThinkPHP/---微信支付PC流程
  12. 使用N2N,实现异地快速组建内网
  13. vivado2020报错:error when launching …vivao.bat…launcher time out“
  14. google map 的地图偏移 火星坐标
  15. Springcloud之OAuth2
  16. 图灵奖得主John Hennessy、David Patterson 访谈:未来小学生都能做机器学习
  17. 新的一周,新的旅程…
  18. Linux音频问题——codec寄存器配置
  19. appium环境搭建-android自动化
  20. 全网精华之C++11 60篇链接汇总

热门文章

  1. 一次 ASAN 找不到 symbolizer 问题的分析与解决
  2. 执教《送给盲婆婆的蝈蝈》有感
  3. chrome 插件 页面请求转发_使用chrome扩展程序及jsonp实现跨域访问
  4. 最公正海贼王实力排行TOP50!
  5. 软件研发模型和软件测试模型
  6. 我的世界服务器bug修复,我的世界服务器bug大全以及修复方法汇总
  7. linux 配置回指路由,不配置回指路由多网段网络如何互联?
  8. 考研英语朱伟推荐的句子,写作文可以用(三)
  9. 冰刃(icesword) V1.22 Final┊官方中文绿色版
  10. 二次型相似对角化的原因