2015-06-26 16:10:41 lfh719852029 阅读数 2606

       之前的项目中有用虚拟摇杆来操纵角色移动,但是之前使用的是EasyTouch,属于NGUI下的一个插件,但是本身项目是基于UGUI的,觉得这样掺杂在一起有些不伦不类,就自己用UGUI做了一个简易的虚拟摇杆(可以实现给角色移动脚本发出摇杆的偏移参数类似于EasyTouch),若要实现更加复杂的功能,可以参照EasyTouch自行编写。

       不多说下面开始实现步骤:(写这篇文章的时候离制作有些久了,有些地方可能有遗忘或者错误请见谅)

一、UGUI上接受拖拽事件。

       我们项目基于Android平台,所以要考虑一些点击,拖动什么的能不能得到相应,然后在网上搜到了这篇帖子:

(雨松MOMO:UGUI研究院之控件以及按钮的监听事件系统(五))原文:http://www.xuanyusong.com/archives/3325


这是监听事件的脚本:EventTriggerListener.cs


using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class EventTriggerListener : UnityEngine.EventSystems.EventTrigger
{
	public delegate void VoidDelegate (GameObject go);
	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;
	}

	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);
	}
}


二、虚拟摇杆的实现

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class JoyStick : MonoBehaviour 
{
	public float fJoyTouchRadius;
	public float fJoyAreaRadius;
	
	private Image joyTouchImage;
	private Image jouAreaImage;

	private bool bIsActived;//是否触发
	public bool BIsActived
	{
		get{ return bIsActived; }
	}
	
	private bool bIsLocked = false;//是否上锁
	public bool BIsLocked
	{
		get{ return bIsLocked; }
		set
		{ 
			bIsLocked = value; 
			if(bIsLocked)
			{
				if(joyTouchImage)
				{
					joyTouchImage.transform.position = initPos;
					//将偏移量归零
					JoyStickAxis = Vector2.zero;
					//角色移动函数
					if(joyStickMoveEnd != null)
						joyStickMoveEnd();
				}
			}
		}
	}
	private float fDragRadius;//可拖动的半径
	private Vector3 initPos;//初始位置
	private float fScreenRate;//屏幕比率
	private Vector2 JoyStickAxis;//用来传值给角色控制器
	
	//角色移动代理函数
	public delegate void JoyStickMove(Vector2 joyStickOffset);
	public static event JoyStickMove joyStickMove;
	
	//角色停止移动代理函数
	public delegate void JoyStickMoveEnd();
	public static event JoyStickMoveEnd joyStickMoveEnd;
	
	void Start () 
	{
		//我这边自己做的自适应,后面的GlobalManager.iScreenWitdh是你默认的屏幕宽度,其实可以用UGUI里面的
		//Horizontal Layout Group来控制自适应
		fScreenRate = (float)Screen.width / GlobalManager.iScreenWitdh;
		joyTouchImage = transform.FindChild("JoyTouch").GetComponent<Image>();
		jouAreaImage = transform.FindChild("JoyArea").GetComponent<Image>();
		
		//设置joystick的大小
		joyTouchImage.rectTransform.localScale = new Vector2 (fJoyTouchRadius, fJoyTouchRadius);
		jouAreaImage.rectTransform.localScale = new Vector2 (fJoyAreaRadius, fJoyAreaRadius);
		
		//可拖拽的半径
		fDragRadius = jouAreaImage.rectTransform.rect.width * fJoyAreaRadius * fScreenRate * 0.5f;
		
		initPos = new Vector3 (1, 1, 0) * fDragRadius * 1.2f;//1.2距离侧边的距离放大1.2倍,这边可以自己后面更改
		
		joyTouchImage.transform.position = initPos;
		jouAreaImage.transform.position = initPos;
		
		//给joytouch注册事件
		EventTriggerListener.Get (joyTouchImage.gameObject).onDown = OnJoyTouchDown;
		EventTriggerListener.Get (joyTouchImage.gameObject).onUp = OnJoyTouchUp;
		
		//给joyarea注册事件
		EventTriggerListener.Get (jouAreaImage.gameObject).onDown = OnJoyTouchDown;
		EventTriggerListener.Get (jouAreaImage.gameObject).onUp = OnJoyTouchUp;
	}
	
	void Update()
	{
		#if UNITY_ANDROID
//		if (Input.touchCount > 1)
//			return;
		//这边会有一个bug就是当你在拖拽摇杆的时候,点击屏幕别的地方,摇杆会向点击处偏移
		//我没有找到很好的解决方法,暂时是用多点触控屏蔽的方法
		#endif
		
		if(bIsActived && !bIsLocked)
		{
			//joy世界坐标坐标
			Vector3 wJoyTouchPos = joyTouchImage.transform.position;
			
			//鼠标屏幕坐标
			Vector3 mScreenPosition = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, wJoyTouchPos.z);
			
			//获得鼠标和对象之间的偏移量,拖拽时相机应该保持不动
			Vector3 offset = wJoyTouchPos - mScreenPosition;		
			
			//对象新坐标 
			joyTouchImage.transform.position -= offset;
			
			//判断与初始位置的距离,之所以不用世界坐标是因为本身半径要转化为世界坐标的半径,麻烦
			float fDis = Vector3.Distance(joyTouchImage.transform.position, initPos);
			
			//当相对位置大于可拖动的半径的时候,按比例缩减
			if(fDis > fDragRadius)
			{
				joyTouchImage.transform.position = initPos + 
					(joyTouchImage.transform.position - initPos) * fDragRadius / fDis;
			}
			
			//虚拟摇杆偏移量
			JoyStickAxis.x = (joyTouchImage.transform.position.x - initPos.x) / fDragRadius;
			JoyStickAxis.y = (joyTouchImage.transform.position.y - initPos.y) / fDragRadius;

			//角色移动函数
			if(joyStickMove != null)
				joyStickMove(JoyStickAxis);
		}
	}
	
	//当触碰虚拟摇杆的进入
	private void OnJoyTouchDown(GameObject go)
	{
		if(go == joyTouchImage.gameObject || go == jouAreaImage.gameObject)
		{
			bIsActived = true;
		}
	}
	
	//当触碰虚拟摇杆的退出
	private void OnJoyTouchUp(GameObject go)
	{
		if(go == joyTouchImage.gameObject || go == jouAreaImage.gameObject)
		{
			bIsActived = false;

			//回到原位
			joyTouchImage.transform.position = initPos;
			//将偏移量归零
			JoyStickAxis = Vector2.zero;

			//角色停止移动函数
			if(joyStickMoveEnd != null)
				joyStickMoveEnd();
		}
	}
}
该脚本的核心处就是Update里面,我写的效率不是很高,可以自行改写。


三、具体操作方式

1.在canvas下创建一个空的JoyStick,该joyStick放在canvas的最下面,否则会被其他控件覆盖掉(UGUI就是这点很烦不能更改控件的Depth)。

2.在joystick下创建2个sprite,给两张转为2dSperite的图片(joyTouch在joyArea的下面)输入缩放大小,本来我这边还有参数设置初始位置,但是后面做自适应找到一个比较适中的值就去掉了,需要的可以自行编写。

3.在角色控制移动脚本处

加入:

void OnEnable()  
{  
	JoyStick.joyStickMove += JoystickMove;
	JoyStick.joyStickMoveEnd += JoystickMoveEnd;
} 

void OnDisable()  
{  
	JoyStick.joyStickMove -= JoystickMove;
	JoyStick.joyStickMoveEnd -= JoystickMoveEnd;
} 
还有就是:

public void JoystickMoveEnd()
{
	//摇杆停止移动响应函数
}
public void JoystickMove(Vector2 joyPos)
{
	//摇杆开始移动响应函数,这边的joyPos就是摇杆偏移量
}

差不多就做好了一个UGUI的简易虚拟摇杆,没有什么扩展功能,没有进行什么优化,如果有什么更好的改进方法请私信我,谢谢!



2017-08-26 19:42:36 yy763496668 阅读数 1864

设置摇杆的背景图片的锚点如下:
这里写图片描述

设置摇杆的锚点为背景图片的中心点。
并给摇杆绑定脚本如下:

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
using System;

public class JoyStickController : MonoBehaviour,IDragHandler,IEndDragHandler {
    //最大的拖动距离
    public float maxDragDistance = 50f;
    //虚拟摇杆的方向
    public Vector3 direction;
    //玩家
    public GameObject player;
    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        //屏幕上的y轴分量 当作游戏世界里的z分量
        //设置玩家的朝向
        player.transform.forward = new Vector3(direction.x,0,direction.y);
        int flag = Vector3.Distance(Vector3.zero, this.transform.localPosition) <1f ? 0 : 1;
        player.transform.Translate(Vector3.forward * flag * Time.deltaTime,Space.Self);

    }
    //拖拽中的时候
    public void OnDrag(PointerEventData eventData)
    {
        this.transform.position = Input.mousePosition;
        if (Vector3.Distance(Vector3.zero,this.transform.localPosition) > maxDragDistance)
        {
            direction = this.transform.position - Vector3.zero;
            this.transform.localPosition = direction.normalized * maxDragDistance;
        }
    }
    //拖拽结束的时候
    public void OnEndDrag(PointerEventData eventData)
    {
        this.transform.localPosition = Vector3.zero;
    }
}

这里写图片描述

2015-10-16 21:33:21 gaojinjingg 阅读数 2247
教程如下: 
英文官方教程链接:http://download.csdn.net/detail/hiramtan/5286699 
英文官方编码目录链接:http://download.csdn.net/detail/hiramtan/5286745 
1.按照下面的指示创建一个虚拟摇杆: 
 
添加后Unity3D游戏预览窗口如下: 
 
然后发现面板中多了下面两项,创建一个空物体,命名为JoystickManager控制虚拟摇杆事件,如下: 
一定要记得第二项"Joystick"物体的名字与JoystickManager.cs代码中的名字一致,下面的步骤会有提示. 
 
查看Joystick属性面板,如下: 
 
 
通过下面的面板,可以将虚拟摇杆更改为自己的图片资源. 


插件下载地址:链接: http://pan.baidu.com/s/1BfCmy 密码: i442      链接: http://pan.baidu.com/s/1eQFHi86 密码: fian

2016-04-16 20:48:06 begonia__z 阅读数 8805

  如今手机游戏玩法多种多样,尤其使用虚拟摇杆进行格斗类游戏开发或者是MMORPG成为了主流的开发方式,可能不少人都会为了制作一个完善的虚拟摇杆感到烦恼,一次又不少人选择使用插件来制作虚拟摇杆。

  Momo大神在不久前才写了一篇用UGUI制作虚拟摇杆的,有兴趣的同学可以到这里看看原文,原文地址:http://www.xuanyusong.com/archives/3924

  今天刚好公司向里面需要用到这个功能,因此我也研究了一下Momo这篇文章,发现里面有一个算是bug的问题因此我就在Momo大神的代码上进行修改,修复了这个问题而且也添加一下功能方便大家去拓展和使用。

  先来看看我和我的小伙伴发现的bug吧。

  下图两张图片是Momo大神的写的UGUI虚拟摇杆的小Demo,我们来看看其效果吧:

   图片一:

   

   图片二:

   

比较一下两张图片你会发现有一个致命问题小球的偏移量出问题了,这种情况只出现在虚拟摇杆距离屏幕边距较近的时候会出现这个问题,远离边距的时候就不会出现这个问题了,来看看远离边距时候的效果吧。如下图所示:


这样的限制对于摇杆功能来说并不会有太大的影响,但是UI的设计上还是会出现一些问题和麻烦的。因此我今天就是为了修复这个问题而对UGUI虚拟摇杆进行了研究,花了一点时间进行了bug的修复,然后拓写了它的功能,方便我们来获取摇杆的偏移量来控制人物移动。

  先来看看我实现的效果吧,废话不多说上图!

  

  上面的图片可以看出来,虚拟摇杆已经贴着屏幕边框了,但是偏移量并没有减少。

  我也不说多少神马了因为实在是太简单了,我就直接上代码吧

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI;
 
public class ScrollCircle :ScrollRect 
{
    public float recoveryTime = 0.1f;
	protected float mRadius;
    protected bool isOnEndDrag = false;
    protected Vector3 offsetVector3 = Vector3.zero;

	void Start()
	{
        inertia = false;
        movementType = MovementType.Unrestricted;
        //计算摇杆块的半径
        mRadius = (transform as RectTransform).sizeDelta.x * 0.5f;
	}

    public override void OnScroll(PointerEventData data)
    {

    }
	public override void OnDrag (PointerEventData eventData)
	{
		base.OnDrag (eventData);
        isOnEndDrag = false;
		var contentPostion = this.content.anchoredPosition;
		if (contentPostion.magnitude > mRadius){
			contentPostion = contentPostion.normalized * mRadius ;
		    SetContentAnchoredPosition(contentPostion);
		}
	}

    public override void OnEndDrag(PointerEventData eventData)
    {
        base.OnEndDrag(eventData);
        if (!isOnEndDrag)
            isOnEndDrag = true;
    }

    void Update()
    {
        UpdateContent();
    }

    /// <summary>
    /// 摇杆小球复位
    /// </summary>
    public void UpdateContent()
    {
        if (isOnEndDrag)
        {
            if (content.localPosition == Vector3.zero)
                isOnEndDrag = false;
            float x = Mathf.Lerp(content.localPosition.x, 0.0f, recoveryTime);
            float y = Mathf.Lerp(content.localPosition.y, 0.0f, recoveryTime);
            content.localPosition = new Vector3(x, y, content.localPosition.z);
        }
        CalculateOffset();
    }
    /// <summary>
    /// 计算偏移量
    /// </summary>
    private void CalculateOffset()
    {
        offsetVector3 = content.localPosition / mRadius;
    }
    /// <summary>
    /// 获取偏移量大小
    /// 偏移量范围是[-1,1]
    /// </summary>
    /// <returns></returns>
    public Vector3 GetOffsetVector3()
    {
        return offsetVector3;
    }
}

这里要注意一点,我在start函数里面修改了movementType,如果没有修改movementType参数的同学是没办法实现这个功能的~切记切记!!

最后上一张图片,红框的位置是要特别注意的地方!


今天写了两篇文章好累啊,我会尽量把自己学到研究到的东西都写出来,可能会很乱很杂大家别介意哈....哈哈哈哈~~

                                                                                                                                                                                                                                                                                           -----Begonia

2018-05-04 21:46:45 weixin_42033401 阅读数 288

虚拟摇杆主要是开发移动端的游戏用的,市面上这样的游戏有很多,大家也都见过或者玩过了,在这给大家介绍两种不同的简易方法制作虚拟摇杆。

一、NGUI做虚拟摇杆

1.首先

将NGUII插件导入Unity中,如图:


         (图1)

链接:https://pan.baidu.com/s/1YhnN5ixxn58HUThoukdTZA  密码:eelw

2.导入NGUI插件后


                                                      (图2)


      (图3)

点击NGUI添加一个Sprite命名为JoyStick并为其添加一个Box Collider(注意:添加Box Collider时要选中该物体并在Scene窗口中点击右键添加,添加的Box Collider是作为触发器使用,所以不要忘记勾选Is  Trigger)如图:


            (图4)

之后再在JoyStick下方创建一个子物体(Sprite)命名为CenterPos,如上图3所示

3.以上工作完成以后

JoyStick物体上添加一个脚本(名字自起),脚本内容如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Move : MonoBehaviour {
    //确定是否按压到屏幕
    public bool IsPress;
    //虚拟摇杆的边缘限制框
    public GameObject JoyStick;
    //虚拟摇杆
    public GameObject CenterPos;
    //触碰的ID值
    public int TouchId;
    public Camera uicamera;
    //虚拟摇杆的边缘限制框Sprite的半径
    public float SpriteWidth;
    private void Awake()
    {
        SpriteWidth = this.gameObject.GetComponent<UISprite>().width/2;
    }
    //NGUI中UIEventTrigger脚本中自带的方法
    void OnPress(bool ispress)
    {
        IsPress = ispress;
       //初始触碰的ID
        TouchId = UICamera.currentTouchID;
       
    }
	
	// Update is called once per frame
	void Update () {

        if (IsPress)
        {
            //1.得到触碰的位置
            Vector2 mousePos = UICamera.GetTouch(TouchId, false).pos;
            //2.虚拟按键的初始位置
            Vector2 JoyStick = new Vector2(uicamera.WorldToScreenPoint(this.transform.position).x, 
                                           uicamera.WorldToScreenPoint(this.transform.position).y);
            //3.触碰点到虚拟按键中心点的距离
            float dis = Vector3.Distance(mousePos, JoyStick);
            //4.如果触碰点到虚拟按键位置的距离>SpriteWidth
            if (dis>SpriteWidth)
            {
                //则虚拟摇杆的相对坐标最大为SpriteWidth
                CenterPos.transform.localPosition = (mousePos - JoyStick).normalized*SpriteWidth;
            }
            else
            {
                //相对的  虚拟摇杆的相对坐标为 触碰点到虚拟按键中心点的相对向量
                CenterPos.transform.localPosition = mousePos - JoyStick;
            }
            //如果想控制玩家的移动加上下面这句代码,这里的Cube代表玩家,玩家的移动速度根据自己的情况调节
            GameObject.Find("Cube").transform.position += new Vector3((mousePos - JoyStick).normalized.x, 0, 
                                                                      (mousePos - JoyStick).normalized.y)*Time.deltaTime*10f;

        }
        else
        {
            //触碰事件一旦结束,虚拟摇杆的位置回归原位置
            CenterPos.transform.localPosition = Vector2.zero;
        }

	}
}

这里对于刚入门的朋友有必要说一下脚本中的normalized(归一化)即:当归一化后,返回向量的长度为1,当归一化后,向量保持同样的方向。

这里的所有对象用Public定义是为了在unity中更清楚的观察对象的变化,大家可以根据自己的喜好定义。NGUI的摇杆制作到这里就结束了。

二、UGUI简易摇杆制作

UGUI的场景搭建和NGUI 的大致相同,区别在于UGUI只需要直接搭建场景,不多说,直接上图:


场景搭建完后需要在Project中导入一个脚本


链接:https://pan.baidu.com/s/19KClAdF-xRbhBvlPA825oQ 密码:vhe7

之后在JoyStick物体上添加一个脚本(名字自起),脚本内容如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class JoyStickTest : MonoBehaviour {
    public GameObject centerObj;//定义出小圆盘
    public float range;//范围
	// Use this for initialization
	void Start () {
        centerObj = transform.Find("CenterObj").gameObject;//赋值
        UIEventTrigger.Get(this.gameObject).onPointerDown = BtnEvent;//.调用uiEventTrigger.get方法
	}
    public void BtnEvent(PointerEventData data)
    {
        StartCoroutine("Move");//调用协程Move
    }
    IEnumerator Move()
    {
        while (Input.GetMouseButton(0))//按下鼠标左键
        {
            Vector3 mousePos = new Vector3(Input.mousePosition.x,Input.mousePosition.y,0);//鼠标的位置
            Vector3 offset = mousePos - this.transform.position;//偏移量
            //给小圆盘坐标赋值
            if (offset.magnitude>range)
            {
                offset = offset.normalized * range;
            }
            //此处为给玩家赋值,控制玩家移动。
            GameObject.Find("Player").transform.position += new Vector3(offset.normalized.x, 0, offset.normalized.y) * Time.deltaTime * 5;

            centerObj.transform.position = this.transform.position+offset;

            yield return null;
        }
        //小圆盘相对坐标归零
        centerObj.transform.localPosition = Vector2.zero;


    }

	// Update is called once per frame
	void Update () {
		
	}
}

此脚本运用了协程控制,不理解的可以查询协程的使用,在此就不多解释了。

到此UGUI的虚拟摇杆制作就完成了。

没有更多推荐了,返回首页