【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

这篇具有很好参考价值的文章主要介绍了【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

嗨,大家好,我是新发。
有同学私信我,问我能不能写一篇Unity手游第一人称视角控制的教程,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
那么,今天就来做个Demo吧~

注:Demo工程源码见文章末尾

最终效果如下:
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

二、实现方案

1、无主之地,第一人称视角

第一人称视角的游戏大家应该不陌生,比如《无主之地》,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
不过它是PC平台的,使用WASD控制移动,使用鼠标来控制镜头角度,单击鼠标左键开枪。注:你也可以接手柄来操作~

那么,如果我们想做移动端(手机端)的第一人称视角,如何做角色控制呢?

2、我之前做的摇杆控制

手机端比较常见的就是摇杆控制了,我之前在几篇博客中都有做过摇杆控制,
《【游戏开发创新】用Unity等比例制作广州地铁,广州加油,早日战胜疫情(Unity | 地铁地图 | 第三人称视角)》
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
《【游戏开发创新】上班通勤时间太长,做一个任意门,告别地铁与塞车(Unity | 建模 | ShaderGraph | 摇杆 | 角色控制)》
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
《【游戏开发实战】新发教你做游戏(六):教你2个步骤实现摇杆功能》
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

3、第一人称视角 + 摇杆控制

我上面做的都是第三人称视角的摇杆控制,我们改成第一人称视角即可,也就是第一人称视角+摇杆控制,像这样子,(图片说明:下图是我在《无主之地》游戏截图中P了摇杆的UI
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

三、开始实战

1、资源获取:Unity AssetStore

我没有《无主之地》的资源,没关系,我们去UnityAssetStore上找一下FPS射击游戏的资源,

注:Unity AssetStore地址:https://assetstore.unity.com/
关于资源的搜索,我之前写过一篇文章:《Unity游戏开发——新发教你做游戏(二):60个Unity免费资源获取网站》

搜索关键字FPS Pack,马上就搜到了一个免费的资源Low Poly FPS Pack

【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
我们点击添加至我的资源(注意需要先登录你的Unity账号),
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
然后回到Unity编辑器中,点击菜单Windows / Package Manager,打开PackageManager窗口,就可以看到我们刚刚在AssetStore中添加的资源啦,我们把资源包下载并导入我们的工程即可。
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

注:你得先创建一个空工程,然后再导入资源包。我之前写过《学Unity的猫》系列教程,其中第三章有讲创建工程的步骤,
《【学Unity的猫】——第三章:第一个Unity工程,你好喵星人》)

2、Low Poly FPS Pack资源运行效果

Low Poly FPS Pack资源包中已经帮我们做好了一个简单的第一人称FPS游戏Demo,我们打开Assault_Rifle_01_Demo场景,如下
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
运行,测试效果如下
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
如你所见,经典的PC平台FPS射击游戏玩法,使用WASD控制移动,使用鼠标来控制镜头角度,单击鼠标左键开枪。
接下来,我们要给它做下手术,改成 摇杆按钮 控制。

3、制作UI界面
3.1、UI素材获取

摇杆图片简单处理,用一个圆就可以了,然后我们还需要一些按钮图标,比如开枪、丢手雷、跳跃、装子弹等,这里推荐我平时经常用的一个查找图标资源的网站,阿里图标库:https://www.iconfont.cn/
比如我搜关键字:枪,就可以看到枪的图标啦~
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
可以直接免费下载,而且还可以事先修改图片颜色,建议改成白色,这样方便在Unity中设置其他颜色,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
根据你自身的需要下载一些图标资源,我下载的图标如下,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
注意,因为我们要在UGUI中显示这些图标,需要将它们的Texture Type设置为Sprite (2D and UI),如下
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

3.2、创建UI摄像机:UICamera

建议UI的显示使用一个单独的摄像机来渲染,我们在场景中创建一个Camera,重命名为UICamera

注:创建摄像机的操作步骤:在Hierarchy视图中鼠标右键,然后点击菜单Camera即可。

【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
设置摄像机的Clear FlagsDepth only,设置Culling Mask只渲染UI层,设置ProjectionOrthographic(正交模式),设置Depth1(确保UI摄像机的比3D摄像机后渲染),
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

3.3、创建UI画布:Canvas

接下来,我们在Hierarchy视图中鼠标右键,点击菜单UI / Canvas,创建一个Canvas【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
设置一下参数,如下,目的是让UICamera来渲染Canvas的内容,并设置分辨率适配规则,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

3.4、创建Panel:GamePanel

我们在Canvas子节点下创建一个Panel,重命名为GamePanel,并把Image组件禁用,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
下面我们再在GamePanel下去创建UI对象。

3.5、制作摇杆

我们先做移动控制的摇杆,在GamePanel子节点下创建一个Image,重命名为moveJointedArm
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
设置左下角对齐,并调整坐标和尺寸,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
像这样,它就是我们摇杆的检测区域,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

我们把它的Image组件的颜色的Alpha通道设置为0,这样我们就看不见它了,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
我们在moveJointedArm子节点下再创建两个Image,分别命名为bgcenter
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
分别设置一下尺寸,图片,颜色,如下
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
效果
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

同理,做一下右摇杆,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
效果
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

3.6、制作操作按钮

除了移动和旋转,我们还有开枪、丢手雷、跳跃、装子弹的操作,配套需要制作对应的按钮。安排上,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
效果
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
到这里,我们的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脚本,并设置Contentcenter节点,如下,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
同理设置右摇杆rotateJointedArm。到此,我们的摇杆就有交互效果了,如下
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

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节点上,并设置变量对象,如下
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

5.3、设置摇杆委托

Start函数中添加摇杆的委托,如下

// GamePanel.cs

void 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.cs

void 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脚本中。
画个图,方便大家理解,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

我们分别在FPSControllerLPFP.cs脚本和AutomaticGunScriptLPFP.cs脚本中添加事件订阅和注销,如下

// FPSControllerLPFP.cs

private 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.cs

private void Start()
{
	// ...
	// 订阅事件
    EventDispatcher.instance.Regist(EventNameDef.MOVE, OnEventMove);
	// ...
}

private void OnDestroy()
{
	// 注销事件
    EventDispatcher.instance.UnRegist(EventNameDef.MOVE, OnEventMove);
	// ...
}
7、移动控制
7.1、流程

流程如下,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

7.2、代码实现

摇杆通过MOVE事件传递了移动方向过来,我们在FPSControllerLPFP.cs脚本中把它缓存到m_moveDirection变量中,如下

// FPSControllerLPFP.cs

Vector3 _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.cs

private 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、运行效果

此时效果
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

8、旋转控制
8.1、流程

同理,旋转控制也是通过事件的响应函数来触发,流程如下,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

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、运行效果

此时效果
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

9、开枪控制
9.1、流程

【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

9.2、封装EventTrigger,监听长按事件

因为开枪是一个连续过程,我们要检测是否长按了开枪按钮,而UGUIButtononClick只能监听点击事件,所以我们需要另外实现长按事件的监听。
UnityEngine.EventSystems命名空间下有个EventTrigger类,它基本提供了所有UI事件,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
我们封装一个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.cs

bool _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、运行效果

此时效果,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

10、丢手雷、跳跃、装子弹

同理,通过事件订阅触发丢手雷、跳跃、装子弹等逻辑。

10.1、丢手雷

【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

10.2、跳跃

【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

10.3、装子弹

【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

11、加个彩蛋,同学你上电视了

给这位提问的同学一次上电视的机会,我把他贴到墙上,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
初始的时候图片半透明,角色靠近的时候图片完全不透明,用到的是触发器,
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)
关于触发器的教程,我之前写过一些文章,《【学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,如果你使用的版本与我的不同,可能会有一些兼容问题。
【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)

五、完毕

好啦,就到这里吧~
我是林新发:https://blog.csdn.net/linxinfa
原创不易,若转载请注明出处,感谢大家~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信~文章来源地址https://www.toymoban.com/news/detail-404460.html

到了这里,关于【游戏开发实战】Unity手游第一人称视角,双摇杆控制,FPS射击游戏Demo(教程 | 含Demo工程源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 第一人称视角控制移动、旋转

    第一人称视角操作 此功能主要是借鉴YouTube上一个博主的教程,写的蛮好我就搬运过来让大家也试试。 其原理是:左右旋转是控制父物体的欧拉角的Y轴 上下点头是子物体相机的欧拉角Z轴 水平方向 跳跃: 移动

    2024年04月27日
    浏览(23)
  • Unity使用NaveMesh实现第一人称视角移动

    Navemesh寻路系列文章 文章目录 目录 系列文章目录 文章目录 前言 一、NavMeshPath是什么? 二、使用步骤 1.引入库 2.读入数据 总结 navemesh已经大量使用到游戏中,但大部分寻路都是使用SetDestination函数,给予一个目标点移动,第一人称直接操控移动的文章很少。 为了解决这个问

    2024年02月03日
    浏览(27)
  • Unity例子——第一人称视角的角色控制器

    本文是为了前文Unity四元组的举例示范,为了让读者更好地理解。 效果是实现一个可以由鼠标进行方向操作,键盘进行移动操作的任务。 此为效果视频: 1687597097844 下面进行教学: 新建一个场景,放置一个plane作为地板,尺寸自定 创建一个新的material挂在plane上 再地板上放置

    2024年02月10日
    浏览(37)
  • Unity3D中实现Player的第一人称视角

    效果,上面为Scene场景,下面为Game场景 0创建地形,当然可以先简单的创建一个空白的Terrain。这里我已经对地形进行了初步的编辑和渲染。 1.在Hierarchy视图中右键创建一个胶囊体(Capsule)作为Player,添加好后重置胶囊体的位置,并且调整胶囊体在一个合适的位置。  2.将Main

    2023年04月08日
    浏览(43)
  • Unity3D中实现人物的第一人称视角

    打开unity创建一个场景地图可以添加一个Plane作为地面 在Hierarchy视图中右键创建一个胶囊体(Capsule)作为Player,添加好后重置胶囊体的位置,并且调整胶囊体在一个合适的位置。 将Main Camera拖拽到到player内作为子对象,重置一下Main Camera的transform,并且再调整一下它在player中

    2024年02月11日
    浏览(45)
  • 【0到1学习Unity脚本编程】第一人称视角的角色控制器

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 :【0到1】Unity脚本游戏开发宝典 将摄像机变为胶囊体的子物体,按住 ctrl +shift +F 调整视野。让他变成胶囊体的眼睛 判断要前进时按照哪个

    2024年02月05日
    浏览(32)
  • Unity3D实现第一人称移动,随鼠标转动视角+上楼梯(C#)

    第一人称移动: 1、在层级面板创建一个Capsule,命名为Player,将层级面板中的相机拖到Player下方 2、重置Player和摄像机的Transform数值(方便调整摄像机在Player上的位置),然后调整摄像机在Player上的位置 大概将摄像机放到Player眼睛的位置即可。 3、创建一个脚本,命名为Came

    2024年02月13日
    浏览(41)
  • Unity游戏源码分享-Unity手游射击横版游戏

    Unity游戏源码分享-Unity手游射击横版游戏   战斗场景 项目地址: https://download.csdn.net/download/Highning0007/88050256

    2024年02月16日
    浏览(35)
  • Unity-第一人称射箭游戏

    欢迎来到我们开发的第一人称射箭游戏!本游戏的设计目标是提供一种真实而又有趣的射箭体验。玩家可以在美丽的自然场景中自由移动,尝试不同的射箭技巧,挑战静态和动态的靶标,以获取高分。我们灵感来源于对射箭运动的热爱,希望通过这个游戏,让玩家感受到驽弓

    2024年02月03日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包