【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
文章目录
- 一、前言
- 二、实现方案
- 1、无主之地,第一人称视角
- 2、我之前做的摇杆控制
- 3、第一人称视角 + 摇杆控制
- 三、开始实战
- 1、资源获取:Unity AssetStore
- 2、Low Poly FPS Pack资源运行效果
- 3、制作UI界面
- 3.1、UI素材获取
- 3.2、创建UI摄像机:UICamera
- 3.3、创建UI画布:Canvas
- 3.4、创建Panel:GamePanel
- 3.5、制作摇杆
- 3.6、制作操作按钮
- 4、摇杆控制脚本:JointedArm.cs
- 4.1、JointedArm.cs脚本代码
- 4.2、挂摇杆脚本,设置成员对象
- 5、关联UI交互事件
- 5.1、定义UI成员:GamePanel.cs
- 5.2、设置UI对象
- 5.3、设置摇杆委托
- 6、事件管理:订阅、注销、抛出
- 6.1、封装事件管理器:EventDispatcher.cs
- 6.2、定义事件名:EventNameDef.cs
- 6.3、抛出事件
- 6.4、订阅事件和注销事件
- 7、移动控制
- 7.1、流程
- 7.2、代码实现
- 7.3、运行效果
- 8、旋转控制
- 8.1、流程
- 8.2、代码实现
- 8.3、运行效果
- 9、开枪控制
- 9.1、流程
- 9.2、封装EventTrigger,监听长按事件
- 9.3、开枪按钮,长按与抬起事件
- 9.4、响应FIRE事件
- 9.5、运行效果
- 10、丢手雷、跳跃、装子弹
- 10.1、丢手雷
- 10.2、跳跃
- 10.3、装子弹
- 11、加个彩蛋,同学你上电视了
- 四、工程源码
- 五、完毕
一、前言
嗨,大家好,我是新发。
有同学私信我,问我能不能写一篇Unity手游第一人称视角控制的教程,
那么,今天就来做个Demo
吧~
注:
Demo
工程源码见文章末尾
最终效果如下:
二、实现方案
1、无主之地,第一人称视角
第一人称视角的游戏大家应该不陌生,比如《无主之地》,
不过它是PC
平台的,使用WASD
控制移动,使用鼠标来控制镜头角度,单击鼠标左键开枪。注:你也可以接手柄来操作~
那么,如果我们想做移动端(手机端)的第一人称视角,如何做角色控制呢?
2、我之前做的摇杆控制
手机端比较常见的就是摇杆控制了,我之前在几篇博客中都有做过摇杆控制,
《【游戏开发创新】用Unity等比例制作广州地铁,广州加油,早日战胜疫情(Unity | 地铁地图 | 第三人称视角)》
《【游戏开发创新】上班通勤时间太长,做一个任意门,告别地铁与塞车(Unity | 建模 | ShaderGraph | 摇杆 | 角色控制)》
《【游戏开发实战】新发教你做游戏(六):教你2个步骤实现摇杆功能》
3、第一人称视角 + 摇杆控制
我上面做的都是第三人称视角的摇杆控制,我们改成第一人称视角即可,也就是第一人称视角+摇杆控制
,像这样子,(图片说明:下图是我在《无主之地》游戏截图中P
了摇杆的UI
)
三、开始实战
1、资源获取:Unity AssetStore
我没有《无主之地》的资源,没关系,我们去Unity
的AssetStore
上找一下FPS
射击游戏的资源,
注:
Unity AssetStore
地址:https://assetstore.unity.com/
关于资源的搜索,我之前写过一篇文章:《Unity游戏开发——新发教你做游戏(二):60个Unity免费资源获取网站》
搜索关键字FPS Pack
,马上就搜到了一个免费的资源Low Poly FPS Pack
,
我们点击添加至我的资源
(注意需要先登录你的Unity
账号),
然后回到Unity
编辑器中,点击菜单Windows / Package Manager
,打开PackageManager
窗口,就可以看到我们刚刚在AssetStore
中添加的资源啦,我们把资源包下载并导入我们的工程即可。
注:你得先创建一个空工程,然后再导入资源包。我之前写过《学Unity的猫》系列教程,其中第三章有讲创建工程的步骤,
《【学Unity的猫】——第三章:第一个Unity工程,你好喵星人》)
2、Low Poly FPS Pack资源运行效果
Low Poly FPS Pack
资源包中已经帮我们做好了一个简单的第一人称FPS
游戏Demo
,我们打开Assault_Rifle_01_Demo
场景,如下
运行,测试效果如下
如你所见,经典的PC
平台FPS
射击游戏玩法,使用WASD
控制移动,使用鼠标来控制镜头角度,单击鼠标左键开枪。
接下来,我们要给它做下手术,改成 摇杆 和 按钮 控制。
3、制作UI界面
3.1、UI素材获取
摇杆图片简单处理,用一个圆就可以了,然后我们还需要一些按钮图标,比如开枪、丢手雷、跳跃、装子弹等,这里推荐我平时经常用的一个查找图标资源的网站,阿里图标库:https://www.iconfont.cn/
比如我搜关键字:枪,就可以看到枪的图标啦~
可以直接免费下载,而且还可以事先修改图片颜色,建议改成白色,这样方便在Unity
中设置其他颜色,
根据你自身的需要下载一些图标资源,我下载的图标如下,
注意,因为我们要在UGUI
中显示这些图标,需要将它们的Texture Type
设置为Sprite (2D and UI)
,如下
3.2、创建UI摄像机:UICamera
建议UI
的显示使用一个单独的摄像机来渲染,我们在场景中创建一个Camera
,重命名为UICamera
,
注:创建摄像机的操作步骤:在
Hierarchy
视图中鼠标右键,然后点击菜单Camera
即可。
设置摄像机的Clear Flags
为Depth only
,设置Culling Mask
只渲染UI
层,设置Projection
为Orthographic
(正交模式),设置Depth
为1
(确保UI
摄像机的比3D
摄像机后渲染),
3.3、创建UI画布:Canvas
接下来,我们在Hierarchy
视图中鼠标右键,点击菜单UI / Canvas
,创建一个Canvas
,
设置一下参数,如下,目的是让UICamera
来渲染Canvas
的内容,并设置分辨率适配规则,
3.4、创建Panel:GamePanel
我们在Canvas
子节点下创建一个Panel
,重命名为GamePanel
,并把Image
组件禁用,
下面我们再在GamePanel
下去创建UI
对象。
3.5、制作摇杆
我们先做移动控制的摇杆,在GamePanel
子节点下创建一个Image
,重命名为moveJointedArm
,
设置左下角对齐,并调整坐标和尺寸,
像这样,它就是我们摇杆的检测区域,
我们把它的Image
组件的颜色的Alpha
通道设置为0
,这样我们就看不见它了,
我们在moveJointedArm
子节点下再创建两个Image
,分别命名为bg
和center
,
分别设置一下尺寸,图片,颜色,如下
效果
同理,做一下右摇杆,
效果
3.6、制作操作按钮
除了移动和旋转,我们还有开枪、丢手雷、跳跃、装子弹的操作,配套需要制作对应的按钮。安排上,
效果
到这里,我们的UI
界面就基本做好啦,下面就是写代码的环节了~
4、摇杆控制脚本:JointedArm.cs
摇杆的逻辑实现,我之前写过一篇文章讲过原理:《Unity使用ScrollRect制作摇杆(UGUI)》,这里我就不过对赘述,直接说下操作流程。
4.1、JointedArm.cs脚本代码
创建一个C#
脚本,重命名为JointedArm.cs
,代码如下:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;// 摇杆逻辑
public class JointedArm : ScrollRect, IPointerDownHandler
{public Action<Vector2> onDragCb;public Action onStopCb;protected float mRadius = 0f;private Transform trans;private RectTransform bgTrans;private Camera uiCam;private Vector3 originalPos;protected override void Awake(){base.Awake();trans = transform;bgTrans = trans.Find("bg") as RectTransform;uiCam = GameObject.Find("UICamera").GetComponent<Camera>();originalPos = trans.localPosition;}void Update(){if (Input.GetMouseButtonUp(0)){//松手时,摇杆复位trans.localPosition = originalPos;this.content.localPosition = Vector3.zero;}}protected override void Start(){base.Start();//计算摇杆块的半径mRadius = bgTrans.sizeDelta.x * 0.5f;}public override void OnDrag(PointerEventData eventData){base.OnDrag(eventData);var contentPostion = this.content.anchoredPosition;if (contentPostion.magnitude > mRadius){contentPostion = contentPostion.normalized * mRadius;SetContentAnchoredPosition(contentPostion);}//Debug.Log("摇杆滑动,方向:" + contentPostion);if(null != onDragCb)onDragCb(contentPostion);}public override void OnEndDrag(PointerEventData eventData){base.OnEndDrag(eventData);//Debug.Log("摇杆拖动结束");if (null != onStopCb)onStopCb();}public void OnPointerDown(PointerEventData eventData){//点击到摇杆的区域,摇杆移动到点击的位置trans.position = uiCam.ScreenToWorldPoint(eventData.position);trans.localPosition = new Vector3(trans.localPosition.x, trans.localPosition.y, 0);}
}
4.2、挂摇杆脚本,设置成员对象
给moveJointedArm
节点挂JointedArm
脚本,并设置Content
为center
节点,如下,
同理设置右摇杆rotateJointedArm
。到此,我们的摇杆就有交互效果了,如下
5、关联UI交互事件
5.1、定义UI成员:GamePanel.cs
我们创建一个GamePanel.cs
脚本,声明UI
对象,如下
using UnityEngine;public class GamePanel : MonoBehaviour
{/// <summary>/// 移动摇杆/// </summary>public JointedArm moveJointedArm;/// <summary>/// 旋转摇杆/// </summary>public JointedArm rotateJointedArm;/// <summary>/// 开枪按钮/// </summary>public GameObject fireBtn;/// <summary>/// 丢手雷按钮/// </summary>public GameObject bombBtn;/// <summary>/// 跳跃按钮/// </summary>public GameObject jumpBtn;/// <summary>/// 装子弹按钮/// </summary>public GameObject bulletBtn;void Start(){// TODO 关联UI交互事件}
}
5.2、设置UI对象
把它挂到GamePanel
节点上,并设置变量对象,如下
5.3、设置摇杆委托
在Start
函数中添加摇杆的委托,如下
// GamePanel.csvoid Start()
{// 移动控制摇杆moveJointedArm.onDragCb = (direction) =>{// TODO 抛出事件};moveJointedArm.onStopCb = () =>{// TODO 抛出事件};// 旋转控制摇杆rotateJointedArm.onDragCb = (direction) =>{// TODO 抛出事件};rotateJointedArm.onStopCb = () =>{// TODO 抛出事件};// ...
}
我们要抛出一些事件,这里要封装一个事件管理器。
6、事件管理:订阅、注销、抛出
6.1、封装事件管理器:EventDispatcher.cs
我在之前的多篇文章中都有到和用到事件管理器,欢迎阅读我之前写的这些文章,里面都有用到事件管理器,
《【游戏开发框架】自制Unity通用游戏框架UnityXFramework,详细教程(Unity3D技能树 | tolua | 框架 | 热更新)》
《【游戏开发创新】用Unity等比例制作广州地铁,广州加油,早日战胜疫情(Unity | 地铁地图 | 第三人称视角)》
《【游戏开发实战】使用Unity 2019制作仿微信小游戏飞机大战(二):搭建基础游戏框架》
《【游戏开发实战】使用Unity制作水果消消乐游戏教程(三):水果拖动与交换逻辑》
《【游戏开发实战】使用Unity制作像天天酷跑一样的跑酷游戏——第七篇:游戏界面的基础UI》
《【学Unity的猫】第十二章:使用Unity制作背包,皮皮的梦想背包》
EventDispatcher
脚本代码:
using UnityEngine;
using System.Collections.Generic;public delegate void MyEventHandler(params object[] objs);/// <summary>
/// 游戏事件管理器
/// </summary>
public class EventDispatcher
{/// <summary>/// 注册事件/// </summary>/// <param name="evt">事件名</param>/// <param name="handler">响应函数</param>public void Regist(string evt, MyEventHandler handler){if (handler == null)return;if (listeners.ContainsKey(evt)){//这里涉及到Dispath过程中反注册问题,必须使用listeners[type]+=..listeners[evt] += handler;}else{listeners.Add(evt, handler);}}/// <summary>/// 注销事件/// </summary>/// <param name="evt">事件名</param>/// <param name="handler">响应函数</param>public void UnRegist(string evt, MyEventHandler handler){if (handler == null)return;if (listeners.ContainsKey(evt)){//这里涉及到Dispath过程中反注册问题,必须使用listeners[type]-=..listeners[evt] -= handler;if (listeners[evt] == null){//已经没有监听者了,移除.listeners.Remove(evt);}}}/// <summary>/// 抛出事件/// </summary>/// <param name="evt">事件名</param>/// <param name="objs">参数</param>public void DispatchEvent(string evt, params object[] objs){try{if (listeners.ContainsKey(evt)){MyEventHandler handler = listeners[evt];if (handler != null)handler(objs);}}catch (System.Exception ex){Debug.LogErrorFormat(szErrorMessage, evt, ex.Message, ex.StackTrace);}}public void ClearEvents(string key){if (listeners.ContainsKey(key)){listeners.Remove(key);}}private Dictionary<string, MyEventHandler> listeners = new Dictionary<string, MyEventHandler>();private readonly string szErrorMessage = "DispatchEvent Error, Event:{0}, Error:{1}, {2}";private static EventDispatcher s_instance;public static EventDispatcher instance{get{if (null == s_instance)s_instance = new EventDispatcher();return s_instance;}}
}
6.2、定义事件名:EventNameDef.cs
我们再创建一个EventNameDef.cs
脚本,用于定义事件名,如下
/// <summary>
/// 事件名定义
/// </summary>
public class EventNameDef
{/// <summary>/// 移动/// </summary>public const string MOVE = "MOVE";/// <summary>/// 旋转/// </summary>public const string ROTATE = "ROTATE";/// <summary>/// 开枪/// </summary>public const string FIRE = "FIRE";/// <summary>/// 丢手榴弹/// </summary>public const string BOMB = "BOMB";/// <summary>/// 跳跃/// </summary>public const string JUMP = "JUMP";/// <summary>/// 装子弹/// </summary>public const string BULLET = "BULLET";
}
6.3、抛出事件
我们回到GamePanel.cs
脚本,在摇杆的委托中抛出事件,
// GamePanel.csvoid Start()
{// 移动控制摇杆moveJointedArm.onDragCb = (direction) =>{EventDispatcher.instance.DispatchEvent(EventNameDef.MOVE, new Vector3(direction.x, 0, direction.y).normalized, true);};moveJointedArm.onStopCb = () =>{EventDispatcher.instance.DispatchEvent(EventNameDef.MOVE, Vector3.zero, false);};// 旋转控制摇杆rotateJointedArm.onDragCb = (direction) =>{EventDispatcher.instance.DispatchEvent(EventNameDef.ROTATE, new Vector3(direction.x, 0, direction.y).normalized);};rotateJointedArm.onStopCb = () =>{EventDispatcher.instance.DispatchEvent(EventNameDef.ROTATE, Vector3.zero);};// ...
}
6.4、订阅事件和注销事件
摇杆抛出的事件,最终的响应逻辑就是角色移动和旋转,那么我们就要在原来控制角色移动和旋转的脚本中添加事件订阅。
逻辑在哪里呢?逻辑在FPSControllerLPFP.cs
脚本和AutomaticGunScriptLPFP.cs
脚本中。
画个图,方便大家理解,
我们分别在FPSControllerLPFP.cs
脚本和AutomaticGunScriptLPFP.cs
脚本中添加事件订阅和注销,如下
// FPSControllerLPFP.csprivate void Start()
{// ...// 订阅事件EventDispatcher.instance.Regist(EventNameDef.MOVE, OnEventMove);EventDispatcher.instance.Regist(EventNameDef.ROTATE, OnEventRotate);// ...
}private void OnDestroy()
{// 注销事件EventDispatcher.instance.UnRegist(EventNameDef.MOVE, OnEventMove);EventDispatcher.instance.UnRegist(EventNameDef.ROTATE, OnEventRotate);// ...
}
// AutomaticGunScriptLPFP.csprivate void Start()
{// ...// 订阅事件EventDispatcher.instance.Regist(EventNameDef.MOVE, OnEventMove);// ...
}private void OnDestroy()
{// 注销事件EventDispatcher.instance.UnRegist(EventNameDef.MOVE, OnEventMove);// ...
}
7、移动控制
7.1、流程
流程如下,
7.2、代码实现
摇杆通过MOVE
事件传递了移动方向过来,我们在FPSControllerLPFP.cs
脚本中把它缓存到m_moveDirection
变量中,如下
// FPSControllerLPFP.csVector3 _moveDirection;
private void OnEventMove(params object[] args)
{_moveDirection = (Vector3)args[0];
}
在FixedUpdate
函数中执行MoveCharacter
方法,在MoveCharacter
方法中根据m_moveDirection
去计算移动,逻辑如下,(部分函数此处没有列出,可下载工程源码进行查看)
// FPSControllerLPFP.cs /// <summary>
/// 移动角色
/// </summary>
private void MoveCharacter()
{// 转为世界坐标系下的方向var worldDirection = transform.TransformDirection(_moveDirection);// 移动速度var velocity = worldDirection * (input.Run ? runningSpeed : walkingSpeed);// 检查碰撞,以便角色在跳墙时不会卡住var intersectsWall = CheckCollisionsWithWalls(velocity);if (intersectsWall){_velocityX.Current = _velocityZ.Current = 0f;return;}// 平滑运算var smoothX = _velocityX.Update(velocity.x, movementSmoothness);var smoothZ = _velocityZ.Update(velocity.z, movementSmoothness);// 获取当前刚体速度var rigidbodyVelocity = _rigidbody.velocity;// 计算速度差var force = new Vector3(smoothX - rigidbodyVelocity.x, 0f, smoothZ - rigidbodyVelocity.z);// 给刚体施加一个力_rigidbody.AddForce(force, ForceMode.VelocityChange);
}
移动的同时,还需要播放走路动画,逻辑在AutomaticGunScriptLPFP.cs
脚本中,
// AutomaticGunScriptLPFP.csprivate Animator anim;
private bool isWalking;private void OnEventMove(params object[] args)
{isWalking = (bool)args[1];
}private void Update()
{// ...if (isWalking && !isRunning){anim.SetBool("Walk", true);}else{anim.SetBool("Walk", false);}// ...
}
7.3、运行效果
此时效果
8、旋转控制
8.1、流程
同理,旋转控制也是通过事件的响应函数来触发,流程如下,
8.2、代码实现
// FpsControllerLPFP.cs/// <summary>
/// 旋转角色
/// </summary>
private void RotateCameraAndCharacter()
{// 平滑运算var rotationX = _rotationX.Update(RotationXRaw, rotationSmoothness);var rotationY = _rotationY.Update(RotationYRaw, rotationSmoothness);// 限制竖直方向的旋转角度:var clampedY = RestrictVerticalRotation(rotationY);_rotationY.Current = clampedY;// 将世界坐标系下的up方向转为相对手臂的局部坐标系下的方向var worldUp = arms.InverseTransformDirection(Vector3.up);// 计算最终角度(四元数)var rotation = arms.rotation *Quaternion.AngleAxis(rotationX, worldUp) *Quaternion.AngleAxis(clampedY, Vector3.left);// 父节点只沿着y轴旋转,容易漏掉此步,如果没有此步,计算移动的时候会出问题transform.eulerAngles = new Vector3(0f, rotation.eulerAngles.y, 0f);// 手臂自由旋转arms.rotation = rotation;
}
8.3、运行效果
此时效果
9、开枪控制
9.1、流程
9.2、封装EventTrigger,监听长按事件
因为开枪是一个连续过程,我们要检测是否长按了开枪按钮,而UGUI
的Button
的onClick
只能监听点击事件,所以我们需要另外实现长按事件的监听。
UnityEngine.EventSystems
命名空间下有个EventTrigger
类,它基本提供了所有UI
事件,
我们封装一个EventTriggerListener.cs
,继承EventTrigger
,如下,
using UnityEngine;
using UnityEngine.EventSystems;/// <summary>
/// UI事件触发器
/// </summary>
public class EventTriggerListener : UnityEngine.EventSystems.EventTrigger
{public delegate void VoidDelegate(GameObject go);public delegate void BoolDelegate(GameObject go, bool state);public delegate void FloatDelegate(GameObject go, float delta);public delegate void VectorDelegate(GameObject go, Vector2 delta);public delegate void ObjectDelegate(GameObject go, GameObject obj);public delegate void KeyCodeDelegate(GameObject go, KeyCode key);public VoidDelegate onClick;public VoidDelegate onDown;public VoidDelegate onEnter;public VoidDelegate onExit;public VoidDelegate onUp;public VoidDelegate onSelect;public VoidDelegate onUpdateSelect;static public EventTriggerListener Get(GameObject go){EventTriggerListener listener = go.GetComponent<EventTriggerListener>();if (listener == null) listener = go.AddComponent<EventTriggerListener>();return listener;}static public EventTriggerListener Get(Transform transform){EventTriggerListener listener = transform.GetComponent<EventTriggerListener>();if (listener == null) listener = transform.gameObject.AddComponent<EventTriggerListener>();return listener;}public override void OnPointerClick(PointerEventData eventData){if (onClick != null) onClick(gameObject);}public override void OnPointerDown(PointerEventData eventData){if (onDown != null) onDown(gameObject);}public override void OnPointerEnter(PointerEventData eventData){if (onEnter != null) onEnter(gameObject);}public override void OnPointerExit(PointerEventData eventData){if (onExit != null) onExit(gameObject);}public override void OnPointerUp(PointerEventData eventData){if (onUp != null) onUp(gameObject);}public override void OnSelect(BaseEventData eventData){if (onSelect != null) onSelect(gameObject);}public override void OnUpdateSelected(BaseEventData eventData){if (onUpdateSelect != null) onUpdateSelect(gameObject);}
}
9.3、开枪按钮,长按与抬起事件
在GamePanel.cs
中添加按钮长按onDown
和按钮抬起onUp
的监听并抛出FIRE
事件,如下
// GamePanel.cs// 开炮
void Start()
{// ...EventTriggerListener.Get(fireBtn).onDown += (btn) =>{EventDispatcher.instance.DispatchEvent(EventNameDef.FIRE, true);};EventTriggerListener.Get(fireBtn).onUp += (btn) =>{EventDispatcher.instance.DispatchEvent(EventNameDef.FIRE, false);};// ...
}
9.4、响应FIRE事件
开枪的逻辑在AutomaticGunScriptLPFP.cs
脚本中,流程如下
// AutomaticGunScriptLPFP.csbool _fire;
private void OnEventFire(params object[] args)
{_fire = (bool)args[0];
}private void Update()
{// ...if (_fire && !outOfAmmo && !isReloading && !isInspecting && !isRunning){if (Time.time - lastFired > 1 / fireRate){lastFired = Time.time;// 执行开枪DoFire();}}// ...
}void DoFire()
{// 以下具体代码见工程代码,此处不展开了// 播放开枪音效// 播放开炮动画// 播放枪口粒子// 实例化子弹并给子弹一个力// 实例化弹壳
}
9.5、运行效果
此时效果,
10、丢手雷、跳跃、装子弹
同理,通过事件订阅触发丢手雷、跳跃、装子弹等逻辑。
10.1、丢手雷
10.2、跳跃
10.3、装子弹
11、加个彩蛋,同学你上电视了
给这位提问的同学一次上电视的机会,我把他贴到墙上,
初始的时候图片半透明,角色靠近的时候图片完全不透明,用到的是触发器,
关于触发器的教程,我之前写过一些文章,《【学Unity的猫】第十章:Unity的物理碰撞,流浪喵星计划》
这里的检测逻辑如下,
using UnityEngine;
using UnityEngine.UI;public class TipsBoard : MonoBehaviour
{public Image board;private void Start() {board.color = new Color(1, 1, 1, 0.3f);}private void OnTriggerEnter(Collider other){if ("Player" != other.tag) return;board.color = new Color(1, 1, 1, 1);}private void OnTriggerExit(Collider other){if ("Player" != other.tag) return;board.color = new Color(1, 1, 1, 0.3f);}
}
四、工程源码
本文工程源码我已上传到CODE CHINA
,感兴趣的同学可自行下载学习,
地址:https://codechina.csdn.net/linxinfa/FirstPersonGame
注:我使用的Unity
版本是2021.1.7f1c1
,如果你使用的版本与我的不同,可能会有一些兼容问题。
五、完毕
好啦,就到这里吧~
我是林新发:https://blog.csdn.net/linxinfa
原创不易,若转载请注明出处,感谢大家~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信~
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)相关推荐
- 《cocos2d-x手机游戏开发实战》直播课程第一期介绍
史上最牛的cocos2d-x课程 <cocos2d-x手机游戏开发实战>直播课程第一期 史上最牛的优惠活动: 报名关东升老师iOS会员送cocos2d-x直播课程,而且终身会员价格只要19 ...
- 基于Python+百度语音的智能语音ChatGPT聊天机器人(机器学习+深度学习+语义识别)含全部工程源码 适合个人二次开发
目录 前言 总体设计 系统整体结构图 系统流程图 运行环境 Python 环境 Pycharm 环境 ChatterBot 环境 模块实现 1. 模型构建 2. 服务器端 3. 客户端 4. 语音录入 ...
- unity实现坦克第一人称视角移动 鼠标发射,位置改变
Holle 好久没更新了,实在想不到更新啥了,就更新下最近痴迷的unity吧. 分为四个板块,实现第三人称视角,上下左右移动,鼠标进行发射炮弹,鼠标改变移动方向 我们的需要准备的工作有:unity 自 ...
- 对金玺曾版《Unity3D手机游戏开发》第三章“第一人称射击游戏”修改,使支持僵尸连续攻击
我个人觉得这本书写的至少很和我口味,而且他的光盘资料也很详尽,比如,一个实例,不仅有一个完整的实现工程,还有一份供作练习的工程(该工程中没有要练习的部分,而资源啥的都有),让人感觉很好. 这本书下载电 ...
- iOS游戏开发之使用 Spritekit 框架和 Swift 的 iOS 2D 太空射击游戏(教程含源码)
实战需求 使用 Spritekit 框架和 Swift 的 iOS 2D 太空射击游戏 本文价值与收获 看完本文后,您将能够作出下面的界面 实战代码 1.主界面 import SpriteKit im ...
- 【Unity游戏开发笔记】手游-涂鸦弹跳开发分析
涂鸦弹跳游戏截图 游戏类型:2D跳跃类 游戏风格:简朴 游戏技术分析: 1.实现玩家弹跳功能 利用2D刚体直接添加一个向上的Impluse冲击力即可,加力之前将刚体velocity速度设置为0 2.游 ...
- 《Unity 5.x游戏开发实战》一1.9 添加一个水平面
本节书摘来异步社区<Unity 5.x游戏开发实战>一书中的第1章,第1.9节,作者: Alan Thorn 译者: 李华峰 责编: 胡俊英,更多章节内容可以访问云栖社区"异步社 ...
- 【游戏开发实战】教你在Unity中实现模型消融化为灰烬飘散的效果(ShaderGraph | 消融 | 粒子系统 | 特效)
文章目录 一.前言 二.ShaderGraph环境准备 三.模型准备:原神角色模型 四.实现思路 1.效果一的实现思路 2.效果二的实现思路 五.ShaderGraph具体实现 1.效果一 1.1.创 ...
- 【Unity】第一人称视角开发
需求 我的需求是在Unity构建一个第一人称视角,实现移动跳跃功能. 主要参考的是这篇博文提供的方案,不过该方案为了只允许一次跳跃,单独在人物底部构建了一个空对象,我无需此限制,因此对其进行了简化. ...
最新文章
- 第二阶段 铁大Facebook——十天冲刺(七)
- python爬虫原理-Python爬虫原理
- 前端学习(2044)vue之电商管理系统电商系统之优化configwebpack和chainwebpack
- 爸爸的素质决定孩子飞多高,爸爸们请反复看!!!
- 数据结构之二叉搜索树
- vba ado返回集合_利用ADO,实现同一文件夹下多个EXCEL工作表的数据汇总
- 用户名 不在 sudoers文件中,此事将被报告。
- python linkedlist,LinkedList在python中的实现
- sql基础教程和mysql基础教程_SQL基础教程第2版 ([日]MICK 著) 中文pdf高清版[22MB]
- Epic Games Launcher 无效驱动器E
- TOPSIS法 —— matlab
- 白纸不够白,签名照片还需要ps去除背景?Python大法半完美解决电子签名背景问题(20行代码去除签名背景)
- [分享解决]你的支付授权失败。请核对你的信息并重试,或尝试其他支付方式。请联系你的银行了解更多信息
- 什么输入法对计算机英语,电脑怎么把英文输入法设置为默认输入法
- gitLab数据备份和恢复
- 带宽、速率(波特率、比特率)和码元宽度简述
- 思科路由器配置GRE隧道
- 408-数据结构-树的应用-哈夫曼树并查集
- Java培训完能不能就业?
- 如何让旧电脑运行更快:升级笔记本组件和操作系统
热门文章
- 2020移动apn接入点哪个快_为什么别人的4g网总比你快? 手机这个设置没开启, 难怪网络...
- python查找文字在图片中的位置_使用Python识别图片中的中/英文字
- Android高级工程师面试题-字节跳动,含BATJM大厂
- Linux系统下查找最近修改过的文件
- python显示图片不连续_python实现连续图文识别
- 集创北方IPO被终止:曾拟募资60亿 小米华为vivo是股东
- 假如宋钦宗赵桓是一代明君,处理奸佞叛贼,开除主降派力排众议,文武并举。
- Excel中IF函数的使用
- msfvenom手册
- java poi导出Excel表,合并单元格