目录
前言
unity现在有很多成熟的摇杆插件,比如easyTouch,功能很完善,使用也很方便,而我们使用工具的时候也要清楚最基本的工具原理。基于此,我尝试着实现了一个简单的摇杆,并利用此摇杆控制人物的移动。
1.实现原理
我们最常见的摇杆无非是两个圆套在一起,外边一个大圆,内部一个小圆,小圆在大圆内部移动,且不超出大圆。
整个摇杆的关键在于怎么让小圆在大圆内部移动且不超出去。
我的做法是尝试以小圆为原点,给一个最大移动半径, 在拖拽的时候检测鼠标/手指接触屏幕的位置 和 最大移动半径之间的大小 如果鼠标/手指触点没有超出最大半径就把触点的位置赋值给小圆 否则 就把小圆最初位置和触点方向上最大半径的点位置赋值给小圆
2.实现过程
首先尝试获取当前触点的ui位置。
注:触点的位置是屏幕坐标位置 。
屏幕坐标:左下角为原点 右上角为最大点。
ui坐标:以屏幕中心为原点, 左下角为最小点(负值),右上角为最大点。
代码如下:
Vector2 GetCurTouchUIPos() {
//获取触点的屏幕坐标
Vector2 touchPos = UICamera.currentTouch.pos;
//获取当前设备屏幕大小和铆定屏幕大小的缩放比例
float screenRate = sceneWidget / Screen.width;
//按照当前实际屏幕比例 获取1280情况下的高度
float hight = sceneWidget * Screen.height / Screen.width;
//touchPos * screenRate 把位置从screen大小换算到1280大小上
// - new Vector2(1280 / 2f, hight / 2f) 把坐标从左下角移到中心
touchPos = touchPos * screenRate - new Vector2(sceneWidget / 2f, hight / 2f);
return touchPos;
}
铆定屏幕大小指的是UIRoot上做的适配的大小
Screen.width和Screen.height 是当前实际的屏幕大小
当屏幕按照长适配之后 可能根据实际的屏幕比例从而影响到 适配的高
比如我现在的是按照长做的适配 适配的大小时1280:720 当我的设备屏幕分辨率为1600:1000的时候
游戏中的屏幕的长度还是1280像素 但是高度会变成 1280/1600/1000=800像素
所以:
//获取设备屏幕大小和铆定屏幕大小的缩放比例
float screenRate = sceneWidget / Screen.width;
//按照当前实际屏幕比例 获取1280情况下的高度
float hight = sceneWidget * Screen.height / Screen.width;
这是为了求出当前设备的实际游戏中的屏幕高度像素
下面从屏幕坐标转到ui坐标
touchPos = touchPos * screenRate - new Vector2(sceneWidget / 2f, hight / 2f);
找到坐标之后直接在拖拽的时候判断一下
public void Moving() {
Debug.Log("移动中");
isMoving = true;
Vector2 touchPos = GetCurTouchUIPos();
Vector2 touchMove = touchPos - startPos;
//方向*距离得出最后位置
Vector2 addPos = startPos + touchMove.normalized * Mathf.Min(touchMove.magnitude, maxRadius);
float addX = addPos.x;
float addY = addPos.y;
forwardPos.localPosition = new Vector3(addX, addY, 0);
}
此时 其中startPos就是小圆的初始位置 forwardPos 就是小圆的tranform 把求出的值赋给小圆就得到了小圆位置
UI设置:
完整代码:
using UnityEngine;
public class LudwigTouch : MonoBehaviour {
public float maxRadius;
public Vector2 startPos;
public Transform forwardPos;
public Transform backPos;
const float sceneWidget = 1280f;
private void Start() {
startPos = forwardPos.localPosition;
}
Vector2 GetCurTouchUIPos() {
//像素坐标就是铆定的大小的坐标
//屏幕坐标(获取的是铆定的屏幕下的)
Vector2 touchPos = UICamera.currentTouch.pos;
//获取当前屏幕大小和铆定屏幕大小的缩放比例
float screenRate = sceneWidget / Screen.width;
//按照当前实际屏幕比例 获取1280情况下的高度
float hight = sceneWidget * Screen.height / Screen.width;
//touchPos * screenRate 把位置从screen大小换算到1280大小上
// - new Vector2(1280 / 2f, hight / 2f) 把坐标从左下角移到中心
touchPos = touchPos * screenRate - new Vector2(sceneWidget / 2f, hight / 2f);
return touchPos;
}
public void Moving() {
Debug.Log("移动中");
Vector2 touchPos = GetCurTouchUIPos();
Vector2 touchMove = touchPos - startPos;
//方向*距离得出最后位置
Vector2 addPos = startPos + touchMove.normalized * Mathf.Min(touchMove.magnitude, maxRadius);
float addX = addPos.x;
float addY = addPos.y;
forwardPos.localPosition = new Vector3(addX, addY, 0);
Vector3 move = new Vector3(touchMove.normalized.x, 0, touchMove.normalized.y);
move = Camera.main.transform.TransformDirection(move);
move.y = 0.0f;
InputManager.Instance.inputMoveDis = move.normalized;
InputManager.Instance.input = true;
}
public void MoveEnd() {
Debug.Log("移动结束");
forwardPos.localPosition = startPos;
}
}
效果:
3.拓展:
我想让这个摇杆出现在我点击的地方 其他时间隐藏
注:点击的有效范围由touch的BoxCollider大小确认 当前我的touch的BoxCollider 在屏幕的左下占大概四分之一屏幕
1.首先需要在Start方法中隐藏touch
private void Start() {
forwardPos.gameObject.SetActive(false);
backPos.gameObject.SetActive(false);
}
2.点击时出现并把位置设置到点击的位置 并考虑屏幕的边界问题(不要让touch有一部分出现在屏幕外)
public void MoveStart() {
if(!isMoving) {
startPos = GetCurTouchUIPos();
startPos = BoundaryDetection(startPos);
forwardPos.gameObject.SetActive(true);
backPos.gameObject.SetActive(true);
localBackPos = startPos;
localForwardPos = startPos;
backPos.localPosition = startPos;
forwardPos.localPosition = startPos;
}
}
Vector2 BoundaryDetection(Vector3 startPos) {
Vector2 realPos = startPos;
//获取当前屏幕大小和铆定屏幕大小的缩放比例
float screenRate = sceneWidget / Screen.width;
//按照当前实际屏幕比例 获取1280情况下的高度
float hight = sceneWidget * Screen.height / Screen.width;
if(startPos.x - maxRadius - boundaryDetectionAddValue < -sceneWidget / 2) {
realPos.x = -sceneWidget / 2 + maxRadius + boundaryDetectionAddValue;
}
if(startPos.y - maxRadius - boundaryDetectionAddValue < -hight / 2)
realPos.y = -hight / 2 + maxRadius + boundaryDetectionAddValue;
return realPos;
}
UI设置
在touch的EvenTrigger中加上
完整代码:
using UnityEngine;
public class LudwigTouch : MonoBehaviour {
public float maxRadius;
public Vector2 startPos;
public Transform forwardPos;
public Transform backPos;
private Vector2 localForwardPos;
private Vector2 localBackPos;
bool isMoving;
const float sceneWidget = 1280f;
public float boundaryDetectionAddValue = 20f;
private void Start() {
forwardPos.gameObject.SetActive(false);
backPos.gameObject.SetActive(false);
}
Vector2 GetCurTouchUIPos() {
//像素坐标就是铆定的大小的坐标
//屏幕坐标(获取的是铆定的屏幕下的)
Vector2 touchPos = UICamera.currentTouch.pos;
//获取当前屏幕大小和铆定屏幕大小的缩放比例
float screenRate = sceneWidget / Screen.width;
//按照当前实际屏幕比例 获取1280情况下的高度
float hight = sceneWidget * Screen.height / Screen.width;
//touchPos * screenRate 把位置从screen大小换算到1280大小上
// - new Vector2(1280 / 2f, hight / 2f) 把坐标从左下角移到中心
touchPos = touchPos * screenRate - new Vector2(sceneWidget / 2f, hight / 2f);
return touchPos;
}
Vector2 BoundaryDetection(Vector3 startPos) {
Vector2 realPos = startPos;
//获取当前屏幕大小和铆定屏幕大小的缩放比例
float screenRate = sceneWidget / Screen.width;
//按照当前实际屏幕比例 获取1280情况下的高度
float hight = sceneWidget * Screen.height / Screen.width;
if(startPos.x - maxRadius - boundaryDetectionAddValue < -sceneWidget / 2) {
realPos.x = -sceneWidget / 2 + maxRadius + boundaryDetectionAddValue;
}
if(startPos.y - maxRadius - boundaryDetectionAddValue < -hight / 2)
realPos.y = -hight / 2 + maxRadius + boundaryDetectionAddValue;
return realPos;
}
public void MoveStart() {
if(!isMoving) {
startPos = GetCurTouchUIPos();
startPos = BoundaryDetection(startPos);
forwardPos.gameObject.SetActive(true);
backPos.gameObject.SetActive(true);
localBackPos = startPos;
localForwardPos = startPos;
backPos.localPosition = startPos;
forwardPos.localPosition = startPos;
}
}
public void Moving() {
Debug.Log("移动中");
isMoving = true;
Vector2 touchPos = GetCurTouchUIPos();
Vector2 touchMove = touchPos - startPos;
//方向*距离得出最后位置
Vector2 addPos = startPos + touchMove.normalized * Mathf.Min(touchMove.magnitude, maxRadius);
float addX = addPos.x;
float addY = addPos.y;
forwardPos.localPosition = new Vector3(addX, addY, 0);
Vector3 move = new Vector3(touchMove.normalized.x, 0, touchMove.normalized.y);
move = Camera.main.transform.TransformDirection(move);
move.y = 0.0f;
}
public void MoveEnd() {
Debug.Log("移动结束");
isMoving = false;
forwardPos.localPosition = localForwardPos;
backPos.localPosition = localBackPos;
forwardPos.gameObject.SetActive(false);
backPos.gameObject.SetActive(false);
}
}