2018-12-02 02:22:32 Mr_Sun88 阅读数 1365

            【Unity】UGUI超级简单的摇杆制作,摇杆控制物体移动


目录

1.效果展示

2.博客介绍

3.具体内容和思路

 (1)摇杆制作

 (2)移动控制

4.资源包下载

5.推送

6.结语



1.效果展示

 

 


2.博客介绍

       制作摇杆有很多途径,例如利用EasyTouch就可以很简单的制作摇杆,但是自己写一个可以更容易的去把控每一个步骤,本篇博客主要介绍了利用UGUI来快速简易的制作一个摇杆,并利用控制摇杆来控制物体移动,博主将非常详细的介绍每个步骤的作用,希望各位同学能有所收获。

做了个Logo-我会慢慢把他弄好看点的。。。。

3.具体内容和思路

 (1)摇杆制作

第一步:导入图片资源并拖入画布设置好层级

 

 

        我们导入了如图所示的两张图片,分别作为摇杆的底图和摇杆的移动中心,位置如图所示,并设置了一个透明图片作为摇杆的点击区域,这里设置好记得将JoyBg设置为不可见的状态,图中为了方便预览,博主未将摇杆隐藏。

第二步:编写摇杆的代码控制

摇杆控制所需要的属性如下:

        /// <summary>
	/// 摇杆背景
	/// </summary>
	private Transform _joyBg;
	/// <summary>
	/// 摇杆中心
	/// </summary>
	private Transform _joyCenter;
	/// <summary>
	/// 摇杆半径
	/// </summary>
	private float _radius;
	/// <summary>
	/// 移动中心
	/// </summary>
	private Vector2 _moveCenter;
	/// <summary>
	/// 鼠标到移动中心的向量
	/// </summary>
	private Vector2 _mouseToCenterVect;
	/// <summary>
	/// 鼠标到中心点的距离
	/// </summary>
	private float _mouseToCenterDistance;
	/// <summary>
	/// 水平获取值
	/// </summary>
	private float _hor;
	/// <summary>
	/// 垂直获取值
	/// </summary>
	private float _ver;
        /// <summary>
	/// 旋转角度
	/// </summary>
	private float _rotAngle;
	

 

属性介绍:

        _joyBg:获取摇杆底图背景的Transform,用于底图背景的现实

        _joyCenter:获取摇杆中心的Transform,用于显示移动方向和位置

        _radius:移动半径,控制摇杆中心的最大移动距离

        _moveCenter:记录摇杆移动中心的位置

        _mouseToCenterVect:起点为移动中心,终点为触摸点的一个向量,用于判断方向和角度

        _mouseToCenterDistance:触摸点到移动中心的距离,用于控制移动距离不超过最大半径

        _hor:摇杆中心的水平移动值

        _ver:摇杆中心的垂直移动值

        _rotAngle:摇杆的旋转角度,用于计算主角的朝向

 

初始化属性:

        void Start ()
	{

		_joyBg = GameObject.Find("Canvas").transform.Find("JoyBg");
		_joyCenter = GameObject.Find("Canvas").transform.Find("JoyBg/JoyCenter");
		_player = GameObject.Find("Player").transform;
		_radius = 100;
	}

 

给摇杆点击区域添加摇杆拖动开始,正在拖动,拖动结束三个事件:

        /// <summary>
	/// 开始拖动
	/// </summary>
	public void OnDragBegain()
	{
		
	}

	/// <summary>
	/// 正在拖动
	/// </summary>
	public void OnDragMove()
	{
		
	}
	
	/// <summary>
	/// 拖动结束
	/// </summary>
	public void OnDragEnd()
	{
		
	}

 

拖动开始逻辑:

	/// <summary>
	/// 开始拖动
	/// </summary>
	public void OnDragBegain()
	{
		//移动中心点赋值
		_moveCenter = Input.mousePosition;
		//显示摇杆
		_joyBg.gameObject.SetActive(true);
		//摇杆背景位置修正到点击位置
		_joyBg.position = _moveCenter;
		//摇杆中心位置修正到点击位置
		_joyCenter.position = _moveCenter;
	}

 

拖动时逻辑:

        /// <summary>
	/// 正在拖动
	/// </summary>
	public void OnDragMove()
	{
		//中心店到触摸点的向量赋值
		_mouseToCenterVect = (Vector2)Input.mousePosition - _moveCenter;
		//中心店到触摸点的距离计算
		_mouseToCenterDistance = Mathf.Clamp(_mouseToCenterVect.magnitude,0,100);
		//摇杆中心的X - 移动中心的x就是水平的变化值,这里 /100 控制_hor在(-1,,1)之间
		_hor = (_joyCenter.position.x - _moveCenter.x)/100;
		//摇杆中心的Y - 移动中心的Y就是垂直的变化值,这里 /100 控制_hor在(-1,,1)之间
		_ver = (_joyCenter.position.y - _moveCenter.y)/100;

		//角度就是 中心店到触摸点的向量 和 2D平面Y轴正方向的夹角,这里只能显示0——180度
		_rotAngle = Vector3.Angle(_mouseToCenterVect, Vector3.up);

		//这里根据_hor的正负来判断摇杆中心是位于移动中心左侧还是右侧,然后修改度数,显示在0——360之间
		if (_hor<0)
		{
			_rotAngle = 360 - _rotAngle;
		}
		//Vector3.forward 以Vector3.up为中心旋转轴,旋转_rotAngle度,这里计算出主角的旋转度数
		_forwardTarget = Quaternion.AngleAxis(_rotAngle, Vector3.up) * Vector3.forward;
	}

 

拖动结束逻辑:

        /// <summary>
	/// 拖动结束
	/// </summary>
	public void OnDragEnd()
	{
		//水平移动值归零
		_hor = 0;
		//垂直移动值归零
		_ver = 0;
		//隐藏摇杆
		_joyBg.gameObject.SetActive(false);
	}

 

写到这里摇杆的内容就完成了。我们看一下效果吧:

 

 (2)移动控制

移动控制所需属性:

        /// 主角
	/// </summary>
	private Transform _player;
	/// <summary>
	/// 目标朝向
	/// </summary>
	private Vector3 _forwardTarget;
	

属性介绍:

       _player:移动控制的主角(在Start中初始化)

       _forwardTarget:主角的朝向 (在OnDragMove中最底赋值)

 

人物移动逻辑:

        private void Update()
	{
		//只有在水平或者垂直值大于0的情况下,主角才移动或旋转
		if (Math.Abs(_hor) > 0||Math.Abs(_ver) > 0)
		{
			//Mathf.Clamp(_mouseToCenterDistance/100,0,1):根据摇杆中心的移动距离判断速度
			//new Vector3(0,0,0.1f*Mathf.Clamp(_mouseToCenterDistance/100,0,1)):在Z轴方向上移动Mathf.Clamp(_mouseToCenterDistance/100,0,1)的距离,限定在(0-1)的大小
			//不停地在Z轴方向上移动Mathf.Clamp(_mouseToCenterDistance/100,0,1)的距离
			_player.position += _player.TransformDirection(new Vector3(0,0,0.1f*Mathf.Clamp(_mouseToCenterDistance/100,0,1)));
			//根据摇杆的旋转设置主角的朝向
			_player.forward = _forwardTarget;

		}
	}

写到这里所有的逻辑都写完了,效果就是我们文章开头所展示的效果了。


4.资源包下载

需要完整代码和演示的下载资源包:https://download.csdn.net/download/mr_sun88/10822046


5.推送

先空着。。


6.结语

        本篇博客只是简单的介绍的了摇杆的制作和控制物体移动,希望各位同学能有所收获和体会,若是真正运用到手机项目当中还会有诸多问题,例如在手机当中应该使用Touch来获取手指的触摸而不是Input.MousePosition,这些问题博主会在之后专门开一篇来介绍Touch类,各位同学若有兴趣可以关注博主的后续文章。另博主能力有限,文中若有错误的地方期望各位看家可以指点交流。

       QQ交流群:806091680(Chinar)

       该群为CSDN博主Chinar所创,推荐一下!我也在群里!

       本文属于原创文章,转载请著名作者出处并置顶!!!!!

2016-04-18 13:28:32 begonia__z 阅读数 3091

前天撸了一个简单的UGUI虚拟摇杆,今天我就利用前天做的虚拟摇杆做了一个简单的摄像机控制器,主要看看UGUI虚拟摇杆是否可以完美的控制移动和旋转。(PS:主要是为接下来的项目做技术测试),手游版的CF的角色控制器也是利用设个方式进行摄像机的控制,利用双摇杆进行角色的前进和旋转,如果你玩过PSV或者是用过手柄控制器就知道啥意思了,接下来我们就来实现这个功能需求。

首先我们先做两个简单的虚拟摇杆,如下图所示:


因为我们是要实现摄像机的移动,因此我就直接在MainCamera上添加脚本,手游CF左边虚拟摇杆是用来控制角色移动,右边是控制角色的射击以及角色的旋转,那么我们现在我们来实现个功能。因为功能是否简单所以我就直接上代码了。代码如下所示:

using System;
using UnityEngine;
using System.Collections;

public class ControlCamera : MonoBehaviour
{

    public float moveSpeed = 100.0f;
    public float rotateSpeed = 1;
    public float minAngleY = -30f;
    public float maxAngleY = 30f;
    public ScrollCircle joyStick1;
    public ScrollCircle joyStick2;
    private Vector2 angleVector2 = Vector2.zero;
	// Update is called once per frame
	void Update ()
	{
	    UpdateCamera();
	}
    /// <summary>
    /// 更新摄像机位置以及旋转角度
    /// </summary>
    public void UpdateCamera()
    {
        if (!joyStick1.GetIsOnEndDrag() && joyStick1.GetOffsetVector3() != Vector3.zero)
        {

            this.transform.localPosition = new Vector3(
                this.transform.localPosition.x + (joyStick1.GetOffsetVector3().x*moveSpeed),
                this.transform.localPosition.y,
                this.transform.localPosition.z + (joyStick1.GetOffsetVector3().y*moveSpeed));
        }

        if (!joyStick2.GetIsOnEndDrag() & joyStick2.GetOffsetVector3() != Vector3.zero)
        {
            angleVector2.x += joyStick2.GetOffsetVector3().x*rotateSpeed;
            angleVector2.y -= joyStick2.GetOffsetVector3().y*rotateSpeed;

            angleVector2.y = ClampAngle(angleVector2.y, minAngleY, maxAngleY);

            Quaternion rotateAngle = Quaternion.Euler(angleVector2.y, angleVector2.x, 0);
            this.transform.localRotation = rotateAngle;
            angleVector2.y = ClampAngle(angleVector2.y, minAngleY, maxAngleY);
        }
    }

    /// <summary>
    /// 限制选择角度
    /// </summary>
    /// <param name="angle"></param>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns></returns>
    static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}
因为代码很简单所以我只是备注了一下函数用法,如果有不懂可以私聊我。

代码可能写的不好,欢迎大神们指导教育0.0

2016/4/18 15:46

刚刚发现有仔细看代码的时候发现一个严重bug,Y轴旋转180度后你会发现所有的移动都反了,这样影响到了操作了因此我们需要把这个严重的bug修复了,这里我只把UpdateCamera函数放出来,修改如下:

 public void UpdateCamera()
    {
        if (!joyStick1.GetIsOnEndDrag() && joyStick1.GetOffsetVector3() != Vector3.zero)
        {
            movePos.x = joyStick1.GetOffsetVector3().x*moveSpeed;
            movePos.z = joyStick1.GetOffsetVector3().y*moveSpeed;
            float y = transform.localRotation.eulerAngles.y;
            movePos = Quaternion.Euler(0, y, 0)*movePos;
            Vector3 currentPoint = new Vector3(transform.localPosition.x + movePos.x, transform.localPosition.y,
                transform.localPosition.z + movePos.z);

            this.transform.localPosition = currentPoint;
        }

        if (!joyStick2.GetIsOnEndDrag() & joyStick2.GetOffsetVector3() != Vector3.zero)
        {
            angleVector2.x += joyStick2.GetOffsetVector3().x*rotateSpeed;
            angleVector2.y -= joyStick2.GetOffsetVector3().y*rotateSpeed;

            angleVector2.y = ClampAngle(angleVector2.y, minAngleY, maxAngleY);

            Quaternion rotateAngle = Quaternion.Euler(angleVector2.y, angleVector2.x, 0);
            this.transform.localRotation = rotateAngle;
            angleVector2.y = ClampAngle(angleVector2.y, minAngleY, maxAngleY);
        }
    }


                                                                                                                                                                                                                                                            ----Begonia

2014-01-16 22:27:37 ycg01 阅读数 3274


按钮制作

按钮我们定义三种状态, 1.按钮区域外,2.按钮区域内,3.按钮被点下

对三种状态可以分别显示不同的图片。


#ifndef SKBUTTON_H_
#define SKBUTTON_H_

#include "SkComm.h"
#include "SkImage.h"

#include <vector>
#include <string>
using namespace std;
namespace sk_park {

typedef enum _SkButtonStatus {
	SK_BUTTON_STATUS_OUT = 1,
	SK_BUTTON_STATUS_IN = 2,
	SK_BUTTON_STATUS_PRESSED = 3,
} SkButtonStatus;

class SkButton {
public:
	/**按钮的位置信息,只作为成员属性,按钮绘制的位置需要在绘制时候指定**/
	Sint32 m_iPosX;
	Sint32 m_iPosY;
	/**按钮是否显示**/
	bool m_bShow;
	/**按钮是否监听事件**/
	bool m_bListen;
	/**鼠标在按钮内的显示**/
	SkSurface m_effectIn;
	/**鼠标在按钮外的显示**/
	SkSurface m_effectOut;
	/**鼠标在按钮按下的显示**/
	SkSurface m_effectPressed;
	/**鼠标当前状态**/
	SkButtonStatus m_status;
	/**按钮标识**/
	int m_iId;
	string m_sName;
	/**按钮覆盖区域-多边形**/
	SkArea m_area;

	SkButton();
	~SkButton();
	void setShow(SkSurface & effectIn, SkSurface & effectOut,
			SkSurface & effectPressed);
	/**处理事件,iPosX+iPosY:按钮当前位置,pData:外传数据**/
	bool handleEvent(Sint32 iPosX, Sint32 iPosY, SkEvent & stEvent,
			void * pData);
	/**回调函数**/
	void setFuncPressed(void (*func_pressed)(void *, SkButton *));
	SkSurface * getShow(Sint64 iTime);
	void setPosition(Sint32 x, Sint32 y);

private:
	bool bPointIn(Sint32 iX, Sint32 iY);
	void (*m_func_pressed)(void *, SkButton * pButton);
};

}

extern sk_park::SkButton g_SkButton;

#endif /* SKBUTTON_H_ */


具体实现

#include "pch.h"

#include "SkButton.h"
using namespace sk_park;

SkButton::SkButton() {
	m_bShow = true;
	m_bListen = true;
	m_status = SK_BUTTON_STATUS_OUT;
	m_func_pressed = NULL;
	m_iId = 0;
}

SkButton::~SkButton() {

}
void SkButton::setFuncPressed(void (*func_pressed)(void *, SkButton *)) {
	m_func_pressed = func_pressed;
}
void SkButton::setShow(SkSurface & effectIn, SkSurface & effectOut,
		SkSurface & effectPressed) {
	m_effectIn = effectIn;
	m_effectOut = effectOut;
	m_effectPressed = effectPressed;
}
bool SkButton::bPointIn(int32_t iX, int32_t iY) {
	return m_area.bPointIn(iX, iY);
}
void SkButton::setPosition(Sint32 x, Sint32 y) {
	m_iPosX = x;
	m_iPosY = y;
}
bool SkButton::handleEvent(int32_t iPosX, int32_t iPosY, SkEvent & stEvent,
		void * pData) {
	//g_SkComm.log("[%s][%d]posx:%d posy:%d ex:%d ey:%d", __FILE__, __LINE__,
	//		iPosX, iPosY, stEvent.iX, stEvent.iY);
	if (!m_bListen) {
		return false;
	}

	bool bRet = false;
	if (stEvent.type.type == SDL_MOUSEMOTION) {
		int32_t iX = stEvent.iX - iPosX;
		int32_t iY = stEvent.iY - iPosY;
		if (m_status == SK_BUTTON_STATUS_IN
				|| m_status == SK_BUTTON_STATUS_OUT) {
			if (bPointIn(iX, iY)) {
				m_status = SK_BUTTON_STATUS_IN;
				bRet = true;
			} else {
				m_status = SK_BUTTON_STATUS_OUT;
			}
		} else if (m_status == SK_BUTTON_STATUS_PRESSED) {
			if (!bPointIn(iX, iY)) {
				m_status = SK_BUTTON_STATUS_OUT;
			}
		}

	} else if (stEvent.type.type == SDL_MOUSEBUTTONDOWN) {
		int32_t iX = stEvent.iX - iPosX;
		int32_t iY = stEvent.iY - iPosY;
		if (bPointIn(iX, iY)) {
			m_status = SK_BUTTON_STATUS_PRESSED;
			bRet = true;
		}
	} else if (stEvent.type.type == SDL_MOUSEBUTTONUP) {
		SkButtonStatus oldStatus = m_status;
		int32_t iX = stEvent.iX - iPosX;
		int32_t iY = stEvent.iY - iPosY;
		if (bPointIn(iX, iY)) {
			m_status = SK_BUTTON_STATUS_IN;
			bRet = true;
		} else {
			m_status = SK_BUTTON_STATUS_OUT;
		}
		if (oldStatus == SK_BUTTON_STATUS_PRESSED) {
			if (m_func_pressed != NULL) {
				m_func_pressed(pData, this);
				bRet = true;
			}
		}
	}
	return bRet;
}

SkSurface * SkButton::getShow(Sint64 iTime) {
	if (m_status == SK_BUTTON_STATUS_IN) {
		return &m_effectIn;
	} else if (m_status == SK_BUTTON_STATUS_PRESSED) {
		return &m_effectPressed;
	} else {
		return &m_effectOut;
	}
}

SkButton g_SkButton;


按钮的点击区域,设一个区域类来管理

class SkArea {
public:
	SkArea();
	~SkArea();
	void clear();
	/**判断点是否在面积内部**/
	bool bPointIn(float fX, float fY);
	/**多边型添加点**/
	void addPoint(float fX, float fY);
private:
	int m_iNum;
	float * m_pFX;
	float * m_pFY;
	/**禁止拷贝构造**/
	SkArea(SkArea & obj);
};

SkArea::SkArea() {
	m_iNum = 0;
	m_pFX = NULL;
	m_pFY = NULL;
}
SkArea::~SkArea() {
	if (m_pFX != NULL) {
		delete[] m_pFX;
		m_pFX = NULL;
	}
	if (m_pFY != NULL) {
		delete[] m_pFY;
		m_pFY = NULL;
	}
	m_iNum = 0;
}
void SkArea::clear() {
	if (m_pFX != NULL) {
		delete[] m_pFX;
		m_pFX = NULL;
	}
	if (m_pFY != NULL) {
		delete[] m_pFY;
		m_pFY = NULL;
	}
	m_iNum = 0;
}
bool SkArea::bPointIn(float fX, float fY) {
	int i, j;
	bool inside = false;
	double polygon_area = 0;
	double trigon_area = 0;

	for (i = 0, j = m_iNum - 1; i < m_iNum; j = i++) {
		polygon_area += m_pFX[i] * m_pFY[j] - m_pFX[j] * m_pFY[i];
		trigon_area += abs(
				fX * m_pFY[i] - fX * m_pFY[j] - m_pFX[i] * fY
						+ m_pFX[i] * m_pFY[j] + m_pFX[j] * fY
						- m_pFX[j] * m_pFY[i]);
	}

	trigon_area *= 0.5;
	polygon_area = abs(polygon_area * 0.5);
	if (fabs(trigon_area - polygon_area) < 1e-7)
		inside = true;
	return inside;
}
void SkArea::addPoint(float fX, float fY) {
	float * pFx = new float[m_iNum + 1];
	float * pFy = new float[m_iNum + 1];
	for (int i = 0; i < m_iNum; i++) {
		pFx[i] = m_pFX[i];
		pFy[i] = m_pFY[i];
	}
	pFx[m_iNum] = fX;
	pFy[m_iNum] = fY;
	if (m_pFX != NULL) {
		delete[] m_pFX;
		m_pFX = NULL;
	}
	if (m_pFY != NULL) {
		delete[] m_pFY;
		m_pFY = NULL;
	}
	m_pFX = pFx;
	m_pFY = pFy;
	m_iNum++;
}


使用示例:

SkImage pic_ch_1;
	SkImage pic_ch_2;

	pic_ch_1.load("pic/ch1.png");
	pic_ch_2.load("pic/ch2.png");
	m_testButton.setShow(pic_ch_1.m_skSurface, pic_ch_1.m_skSurface,
			pic_ch_2.m_skSurface);
	m_testButton.m_area.addPoint(0, 0);
	m_testButton.m_area.addPoint(0, pic_ch_1.m_iHeight);
	m_testButton.m_area.addPoint(pic_ch_1.m_iWidth, pic_ch_1.m_iHeight);
	m_testButton.m_area.addPoint(pic_ch_1.m_iWidth, 0);
	m_testButton.setPosition(400, 400);


此时按钮显示已经做完了,我们需要在屏幕的点击和移动事件中,调用事件处理函数。

void ViewFirst::doEvent(SkEvent & stSkEvent, Sint64 iCurMTime) {
	bool bRet = m_testButton.handleEvent(m_testButton.m_iPosX,
			m_testButton.m_iPosY, stSkEvent, NULL);
	if (bRet) {
		return;
	}
}


效果如下:


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