Unity本身不提供摇杆的组件,开发者可以使用牛逼的EasyTouch插件或者应用NGUI实现相关的需求,下面本文通过Unity自身的UGUI属性,实现虚拟摇杆的功能。 主参考 《Unity:使用 UGUI 的 ScrollRect 製作虛擬搖桿》和松神的《UGUI研究院之游戏摇杆》,分“摇杆UI的构建”和“摇杆事件连接”以及“摇杆表现强化”三方面总结制作过程中的思路笔记。

  一 摇杆UI的搭建

  核心是使用UGUI 的 ScrollRect Component( 经常用于 Scroll View 的制作),因能响应拖动回弹,同时又能计算对象的X轴和Y轴的偏移,所以选择此组件开发。 创建两Image组件,分别显示摇杆背景和摇杆圆点,

  (overlay保证是场景摄像机看到的最上层)

  (摇杆圆点以底座节点作parent,重置位置)

  给stickBg_添加ScrollRect组件(实质是一脚本),并在 Inspector 视窗將 Content 栏位拖动设置stick_为其内容,

  

  注意,Movement Type 项,将此栏位设置为 Elastic,英文意思是回弹,则能自动将设置在 Content 的物件拉回置中,拖动原件,會有一定程度的缓冲力使物体不被拉远,並在放开时自动弹回置中。

  运行游戏,发现虽然能拖能回弹,但是只能矩形拖动,而且矩形区域贼大,不符合实际项目的摇杆需求。 参考松神博文,直接继承重写ScrollRect组件,代码如下:

using UnityEngine;
using System.Collections;
using UnityEngine.UI; //ScrollRect组件定义在UI的命名空间
using UnityEngine.EventSystems; public class ScrollCircle : ScrollRect
{
protected float mRadius; protected override void Start()
{ this.mRadius = (transform as RectTransform).sizeDelta.x * 0.5f;
} public override void OnDrag(PointerEventData eventData)
{ base.OnDrag(eventData); Vector2 contentPostion = base.content.anchoredPosition; if (contentPostion.magnitude > this.mRadius)
{ contentPostion = contentPostion.normalized * this.mRadius;
} base.content.anchoredPosition = contentPostion;
}
}

  运行游戏,发现拖动半径生效。

  【补料:

     (直接上传给大家凑合着用,亲身找过资源,都要钱不好找哈哈)】

  还有一隐藏bug,就是当摇杆位置布置得比较靠近屏幕边缘时,拖动会出现拖不尽的问题,可参考 《Unity3D学习日记(一)使用UGUI制作虚拟摇杆

  二 摇杆事件连接

  在摇杆UI制作完毕后,接下来监听和派发摇杆的触摸事件,即监听接收到stickBg_的移动,且将移动量传递出去给目标对象。这就运用到unity的事件系统的API,如下:

using UnityEngine;
//using System.Collections; //可去,使用 Coroutine 才需要此声明空间
using UnityEngine.Events; //事件派发Interface
using UnityEngine.EventSystems; //触摸begin drag end事件监听

  在操作摇杆时,主要监听“开始控制( OnBeginDrag )” 和 “控制中( OnDrag )” 以及 “控制结束( OnEndDrag )”三个事件。 接口定义在Event空间的  IDragHandler , IEndDragHandler , IBeginDragHandler类声明中,所以摇杆脚本类要继承这三者类,详细类分析参考官网的 IDragHandler ,

public class FixedJoystickHandler : MonoBehaviour, IDragHandler, IEndDragHandler, IBeginDragHandler
{
public void OnBeginDrag(PointerEventData eventData) {}
public void OnDrag(PointerEventData eventData) {}
public void OnEndDrag(PointerEventData eventData) {}
}

  然后,定义 UnityEvent 事件执行处理对象,负责接收目前操作摇杆的结果,简单说是当监听到上述事件时要对哪个对象执行什么功能,有点类似于UGUI的 Button ,在 Inspector 窗口有Click 事件项,用以设置当发生Click 时会调用执行哪个对象的哪个接口,

  

  官方UnityEvent使用例程如下:

public class ExampleClass : MonoBehaviour
{
UnityEvent m_MyEvent; void Start()
{
if (m_MyEvent == null)
m_MyEvent = new UnityEvent(); m_MyEvent.AddListener(Ping);
} void Update()
{
if (Input.anyKeyDown && m_MyEvent != null)
{
m_MyEvent.Invoke();
}
} void Ping()
{
Debug.Log("Ping");
}
}
    public Transform content;        //摇杆位移量“主人”
public UnityEvent beginControl; //开始控制器
public UnityEvent endControl; //结束控制器

  注意,stickBg_实时位移时需要将真实的位移量传递给UnityEvent类对象以计算移动距离和方向等,即需要传递参数,这就要运用到 UnityEvent扩展的模板类 UnityEvent<T0> ,官方例程如下:

using UnityEngine;
using UnityEngine.Events; [System.Serializable]
public class MyIntEvent : UnityEvent<int>
{
} public class ExampleClass : MonoBehaviour
{
public MyIntEvent m_MyEvent; void Start()
{
if (m_MyEvent == null)
m_MyEvent = new MyIntEvent(); m_MyEvent.AddListener(Ping);
} void Update()
{
if (Input.anyKeyDown && m_MyEvent != null)
{
m_MyEvent.Invoke();
}
} void Ping(int i)
{
Debug.Log("Ping" + i);
}
}

  可见T0表示传递的参数类型,且支持多个形参传递,如本节中传递的位移量是Vector3,

  

    [System.Serializable]  //这里加Serializable目的是使其正确显示在Inspector的事件属性窗口,如下图
public class VirtualJoystickEvent : UnityEvent<Vector3> { }
   public VirtualJoystickEvent controlling;

  

  最后,就是实例化每个事件监听行为,如刚开始被拖动时执行 beginControl 事件等,

    public void OnBeginDrag(PointerEventData eventData)
{ this.beginControl.Invoke();
}
public void OnEndDrag(PointerEventData eventData)
{ this.endControl.Invoke();
}

  还有一点要注意的,本节 controlling 传递的结果是摇杆的偏移量和方向,stick_ 是 stickBg_的子控件,初始本地坐标localPosition为(0,0,0),移动时 x > 0 表示向右,< 0 是向左,而 y > 0 向上。但是随着摇杆资源的尺寸大小不同,若使用实际的偏移量会因不同大小差异而变化,可能会造成影响。 故进行 归一化 运算,使其x和y值总是介于-1到之间,从而实现不管各种摇杆资源尺寸改变,被控制的物体接收到的值都是一样的。

    public void OnDrag(PointerEventData eventData)
{ if (this.content)
{
this.controlling.Invoke(this.content.localPosition.normalized);
}
}

  到此事件监听和派发实现完毕,附上完整脚本的代码:

//FixedJoystickHandler.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems; public class FixedJoystickHandler : MonoBehaviour, IDragHandler, IEndDragHandler, IBeginDragHandler
{ [System.Serializable]
public class VirtualJoystickEvent : UnityEvent<Vector3> { } public Transform content;
public UnityEvent beginControl;
public VirtualJoystickEvent controlling;
public UnityEvent endControl; public void OnBeginDrag(PointerEventData eventData)
{ this.beginControl.Invoke();
} public void OnDrag(PointerEventData eventData)
{ if (this.content)
{
this.controlling.Invoke(this.content.localPosition.normalized);
}
} public void OnEndDrag(PointerEventData eventData)
{ this.endControl.Invoke();
}
}

  运行游戏前,先添加事件处理器controller,对事件接收对象进行赋值,如下图:

  

  

  本节将3d模型预制体对象p4赋值给各个control,响应的是Tank.cs中的对应接口方法。 当然控制目标也可以为人物角色,平时站在原地不移动的动画播放状态为闲置中Idle,当操作摇杆时通知该角色 Animator 开始移动,让 Animator 的动作状态切换为走路动画。然后controling控制中的事件不断通知角色更新当前最新的方向和位移;同理在结束控制后通知角色的 Animator 停止移动,重新切换回原先Idle的动画播放。

  //Tank.cs
  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;   public class Tank : MonoBehaviour {
public List<AxleInfo> axleInfos;
private float motor_ = ;
public float maxMotor;
private float steer_ = ;
public float maxSteer;
private float brake_ = ;
public float maxBrake = ;
private Transform wheelList; private void joyStickControl(float x, float y)
{
steer_ = x * maxSteer;
motor_ = y * maxMotor;
} private void playerCtrl()
{
//steer_ = Input.GetAxis("Horizontal") * maxSteer;
//motor_ = Input.GetAxis("Vertical") * maxMotor;
brake_ = ;
foreach (AxleInfo axleInfo in axleInfos)
{
if (axleInfo.leftWheel.rpm > && motor_ < ) //正在前进时“down”
brake_ = maxBrake;
else if (axleInfo.leftWheel.rpm < - && motor_ > ) //正在后退时“up”
brake_ = maxBrake;
continue;
}
} void Update () {
playerCtrl();
foreach(AxleInfo axleInfo in axleInfos)
{
if (axleInfo.motor)
{
axleInfo.leftWheel.motorTorque = motor_;
axleInfo.rightWheel.motorTorque = motor_;
}
if(axleInfo.steering)
{
axleInfo.leftWheel.steerAngle = steer_;
axleInfo.rightWheel.steerAngle = steer_;
}
axleInfo.leftWheel.brakeTorque = brake_;
axleInfo.rightWheel.brakeTorque = brake_;
if (axleInfos[] != null && axleInfo == axleInfos[])
{
WheelRotation(axleInfos[].leftWheel);
}
}
} private void WheelRotation(WheelCollider collider)
{
if (wheelList == null)
return;
Vector3 pos;
Quaternion rot;
collider.GetWorldPose(out pos, out rot);
foreach(Transform w in wheelList)
{
w.rotation = rot;
}
} private void Start()
{
wheelList = transform.Find("WheelList");
} public void beginMove()
{
Debug.Log("start begin move!!");
} public void doMove(Vector3 drag)
{
joyStickControl(drag.x, drag.y);
} public void endMove()
{
joyStickControl(,);
}
}

  运行游戏,可看到坦克能通过摇杆和按键盘一样的操纵了~

  资料链接:

  《Unity3d使用UGUI开发原生虚拟摇杆

  《Unity3D游戏开发之使用EasyTouch虚拟摇杆控制人物移动

  《教你一步步实现一个虚拟摇杆

  三 摇杆表现强化

  占坑

  (摇杆半透明化,点击拖动的时候全体化)

  (摇杆一开始隐藏,触摸时才实际点位置显示,即浮动式虚拟摇杆)

Unity3d项目入门之虚拟摇杆的更多相关文章

  1. Unity3d项目入门之Rolling Ball

    下面通过分析制作一个简单的收集特定物体的滚球游戏来入门unity,包括操作面板和C#脚本的编写导入,创建Game Object和给Object添加组件等等. 一 初始设置 在Assert下创建主场景M ...

  2. Unity3d项目入门之打Apk包

    ②结合Android Studio编译器打安卓包 在安卓官网下载AS,按照步骤正常安装编译器完毕,运行AS,点击右下图的图标打开SDK Manager, 选择下载安装相关的“SDK Platform” ...

  3. [Unity3D]Unity3D游戏开发之使用EasyTouch虚拟摇杆控制人物移动

    大家好,欢迎大家关注我的博客,我是秦元培,我的博客地址是blog.csdn.net/qinyuanpei.今天呢,我们来一起学习在Unity3D中使用EasyTouch虚拟摇杆来控制人物移动.虽然Un ...

  4. EasyTouch中虚拟摇杆的使用EasyJoystick

    unity3d自带的虚拟摇杆显然没有EasyTouch好用 首先下载这个插件 http://pan.baidu.com/s/1hqJAbTa 下载完成后.导入到unity,可以看看里面的案例

  5. 【转】Unity3D学习日记(二)使用UGUI制作虚拟摇杆控制摄像机

    http://blog.csdn.net/begonia__z/article/details/51178907 前天撸了一个简单的UGUI虚拟摇杆,今天我就利用前天做的虚拟摇杆做了一个简单的摄像机控 ...

  6. Unity3D学习笔记(二十三):事件接口、虚拟摇杆、层级管理和背包系统

    事件接口 IDragHandler(常用):鼠标按下拖动时执行(只要鼠标在拖动就一直执行) IDropHandler:对象拖动结束时,如果鼠标在物体的范围内,执行一次(依赖于IDragHandler存 ...

  7. 【转】Unity3D学习日记(一)使用UGUI制作虚拟摇杆

    http://blog.csdn.net/begonia__z/article/details/51170059 如今手机游戏玩法多种多样,尤其使用虚拟摇杆进行格斗类游戏开发或者是MMORPG成为了主 ...

  8. 转 猫都能学会的Unity3D Shader入门指南(二)

    猫都能学会的Unity3D Shader入门指南(二) 关于本系列 这是Unity3D Shader入门指南系列的第二篇,本系列面向的对象是新接触Shader开发的Unity3D使用者,因为我本身自己 ...

  9. Unity3D Shader入门指南(二)

    关于本系列 这是Unity3D Shader入门指南系列的第二篇,本系列面向的对象是新接触Shader开发的Unity3D使用者,因为我本身自己也是Shader初学者,因此可能会存在错误或者疏漏,如果 ...

随机推荐

  1. openj9

       下面部分转自:https://www.jianshu.com/p/916b5fcd0140 OpenJ9,OMR与OpenJDK Eclipse OpenJ9 是一个 Java 虚拟机(JVM) ...

  2. ROS多根adsl叠加负载均衡PCC的做法

    命令行: / ip firewall mangle1.保证访问局域网IP的时候不被PCC了.add chain=prerouting dst-address=10.1.1.0/24 action=ac ...

  3. 数据库-PLSQL登录oracle数据库卡死(未响应)解决方法

    上次重装系统后重新安装了oracle和PLSQL,哪知道PLSQL登录不了,一登录就未响应,但如果不登录就不卡死,直接就进去了.在网上查了很多解决方法,都没有用. 后来在百度文库找到解决办法,重启Or ...

  4. 软件推荐----截图软件Snagit

    截图软件,比较推荐使用Snagit,功能上所有截图软件有的他都有.Snagit编辑器有一个[库]功能,可以按日期以及应用程序对截图进行分类整理,最赞的是你可以把整个库备份导出,在新的电脑上进行导入,所 ...

  5. 刘志梅2017710101152.《面向对象程序设计(java)》第十三周学习总结

    实验十三  图形界面事件处理技术 实验时间 2018-11-22 1.理论知识 (1)任何支持GUI的操作环境都要不断地监视按键或点击鼠标这样的事件. JAVA这样的面向对象语言,都将事件的相关的信息 ...

  6. 【原创】Linux系统不能上互联网情况下软件更新安装方法

    实际工作中,遇到了Linux 服务器在一台被隔离互联网的PC服务器上运行,使用Virtual Box,在解决此Linux更新的问题上,自己尝试了如下几种策略: 1.将DVD安装盘作为Linux更新源, ...

  7. SSM框架指的是什么

    SSM(Spring+SpringMVC+MyBatis)框架集由Spring.SpringMVC.MyBatis三个开源框架整合而成, 常作为数据源较简单的web项目的框架. 其中spring是一个 ...

  8. GPIO输入输出各种模式(推挽、开漏、准双向端口)详解

    转自:https://blog.csdn.net/techexchangeischeap/article/details/72569999 概述 能将处理器的GPIO(General Purpose ...

  9. Spring声明式事务的隔离级别和传播机制

    声明式事务 在Spring中,声明式事务是用事务参数来定义的.一个事务参数就是对事务策略应该如何应用到某个方法的一段描述,如下图所示一个事务参数共有5个方面组成: 传播行为 事务的第一个方面是传播行为 ...

  10. redis-大key寻找

    使用redis-rdb-tools 项目地址 https://github.com/sripathikrishnan/redis-rdb-tools 生成csv命令 rdb -c memory //d ...