ETC Joystick 继承自 ETCBase类, 并且要实现 PointerEnterHandler IDragHandler, IBeginDragHander IPointerDownHandler IPointerUpHandler,等接口。
ETCJoystick 类
/***********************************************
EasyTouch Controls
Copyright © 2016 The Hedgehog Team
http://www.thehedgehogteam.com/Forum/
The.Hedgehog.Team@gmail.com
**********************************************/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
[System.Serializable]
public class ETCJoystick : ETCBase,IPointerEnterHandler,IDragHandler, IBeginDragHandler, IPointerDownHandler, IPointerUpHandler {
//事件定义
#region Unity Events
[System.Serializable] public class OnMoveStartHandler : UnityEvent{} // 开始移动的时候,
[System.Serializable] public class OnMoveSpeedHandler : UnityEvent { } //滑动 速度, 泛型 带 Vector2 参数
[System.Serializable] public class OnMoveHandler : UnityEvent { }// 在移动时候, 带Vector2 参数
[System.Serializable] public class OnMoveEndHandler : UnityEvent{ }// 移动结束的时候, 无参数
[System.Serializable] public class OnTouchStartHandler : UnityEvent{} // 触摸开始的时候 无参数
[System.Serializable] public class OnTouchUpHandler : UnityEvent{ }// 触摸 抬起(结束) 的时候, 无参数
[System.Serializable] public class OnDownUpHandler : UnityEvent{ } // 按住 上的时候 , 无参数
[System.Serializable] public class OnDownDownHandler : UnityEvent{ } //按住下的时候
[System.Serializable] public class OnDownLeftHandler : UnityEvent{ }//按住左的时候
[System.Serializable] public class OnDownRightHandler : UnityEvent{ }//按住右的时候
[System.Serializable] public class OnPressUpHandler : UnityEvent{ } //按上的时候
[System.Serializable] public class OnPressDownHandler : UnityEvent{ } //按下的时候
[System.Serializable] public class OnPressLeftHandler : UnityEvent{ }//安左的时候
[System.Serializable] public class OnPressRightHandler : UnityEvent{ }//按右的时候。
//定义 声明 委托
[SerializeField] public OnMoveStartHandler onMoveStart; //移动开始的 委托
[SerializeField] public OnMoveHandler onMove; // 移动的时候 的 委托
[SerializeField] public OnMoveSpeedHandler onMoveSpeed; //在移动 速度时 的委托
[SerializeField] public OnMoveEndHandler onMoveEnd; //在移动 结束 的委托
[SerializeField] public OnTouchStartHandler onTouchStart;// 触摸开始的 委托
[SerializeField] public OnTouchUpHandler onTouchUp;//触摸 抬起 的 委托
[SerializeField] public OnDownUpHandler OnDownUp; // 按住上的 委托
[SerializeField] public OnDownDownHandler OnDownDown;//按住下的 委托
[SerializeField] public OnDownLeftHandler OnDownLeft; //以下类同。。。。
[SerializeField] public OnDownRightHandler OnDownRight;
[SerializeField] public OnDownUpHandler OnPressUp;
[SerializeField] public OnDownDownHandler OnPressDown;
[SerializeField] public OnDownLeftHandler OnPressLeft;
[SerializeField] public OnDownRightHandler OnPressRight;
#endregion
//列举
#region Enumeration
public enum JoystickArea { UserDefined,FullScreen, Left,Right,Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight}; //移动摇杆的 区域面积。 自定义,全屏,上下左右中的组合。
public enum JoystickType {Dynamic, Static};// 移动摇杆 类型, 动态 静态, 区别 动态的时候,不固定它的位置,在 触摸屏幕的时候,就显示 摇杆,静态就是固定好在一个位置
public enum RadiusBase {Width, Height, UserDefined}; //半径基于 width,height 这个 属性就是 决定 移动摇杆的Thumb的 移动角度
#endregion
#region Members
#region Public members
public JoystickType joystickType;//移动摇杆类型, 默认 静态
public bool allowJoystickOverTouchPad; //允许 虚拟摇杆 TouchPad, 在FPS中,TouchPad 用来控制 镜头旋转,
public RadiusBase radiusBase; //半径基于 默认 基于width
public float radiusBaseValue; //半径基于值
public ETCAxis axisX; // ETCAxis x轴
public ETCAxis axisY;// ETCAxis y轴
public RectTransform thumb; //中间的 圆 滑杆拇指
public JoystickArea joystickArea;//移动摇杆 区域
public RectTransform userArea; // 用户 区域
public bool isTurnAndMove = false;//允许 旋转与移动
public float tmSpeed = 10; //tm速度
public float tmAdditionnalRotation = 0;//tm附加旋转
public AnimationCurve tmMoveCurve;//tm移动 动画 曲线, 调整 移动 和 旋转
public bool tmLockInJump = false;// tm 锁跳
private Vector3 tmLastMove;//tm 最后的移动速度
#endregion
#region Private members
private Vector2 thumbPosition;// 中间 拇指位置
private bool isDynamicActif; //
private Vector2 tmpAxis;//Tmp轴
private Vector2 OldTmpAxis;//老 tmp轴
private bool isOnTouch;// 是否 触摸
#endregion
#region Joystick behavior option
[SerializeField]
private bool isNoReturnThumb;//是否 不返回 拇指, 勾选后,中间thumb 在拖动后 不会去
public bool IsNoReturnThumb {
get {
return isNoReturnThumb;
}
set {
isNoReturnThumb = value;
}
}
private Vector2 noReturnPosition; // 不返回的 位置, 根据这个位置与原来的位置的距离,去 计算 移动的大小
private Vector2 noReturnOffset;// 不返回的Thumb的 偏移
[SerializeField]
private bool isNoOffsetThumb;//是否 无偏移
public bool IsNoOffsetThumb {
get {
return isNoOffsetThumb;
}
set {
isNoOffsetThumb = value;
}
}
#endregion
#region Inspector
#endregion
#endregion
#region Constructor
//构造函数 默认值
public ETCJoystick(){
joystickType = JoystickType.Static;
allowJoystickOverTouchPad = false;
radiusBase = RadiusBase.Width;
axisX = new ETCAxis("Horizontal");
axisY = new ETCAxis("Vertical");
_visible = true;
_activated = true;
joystickArea = JoystickArea.FullScreen;
isDynamicActif = false;
isOnDrag = false;
isOnTouch = false;
axisX.unityAxis = "Horizontal";
axisY.unityAxis = "Vertical";
enableKeySimulation = true;
isNoReturnThumb = false;
showPSInspector = false;
showAxesInspector = false;
showEventInspector = false;
showSpriteInspector = false;
}
#endregion
//Monobehaviours 回调 赋值Transform RectTransform 在ETCInput,注册控制, 设置 可见, 判断 移动摇杆类型 设置锚点
#region Monobehaviours Callback
protected override void Awake (){
base.Awake ();
if (joystickType == JoystickType.Dynamic){
this.rectTransform().anchorMin = new Vector2(0.5f,0.5f);
this.rectTransform().anchorMax = new Vector2(0.5f,0.5f);
this.rectTransform().SetAsLastSibling();
visible = false;
}
if (allowSimulationStandalone && enableKeySimulation && !Application.isEditor && joystickType!=JoystickType.Dynamic){
SetVisible(visibleOnStandalone);
}
}
public override void Start(){
//初始化 轴
axisX.InitAxis();
axisY.InitAxis();
if (enableCamera){
//初始化 摄像机跟随
InitCameraLookAt();
}
//初始化 轴位置
tmpAxis = Vector2.zero;
OldTmpAxis = Vector2.zero;
// 赋予拇指 初始 位置
noReturnPosition = thumb.position;
pointId = -1;
//动态 就隐藏,
if (joystickType == JoystickType.Dynamic){
visible = false;
}
//找到 摄像机 连接 摄像机, 位置信息
base.Start();
// Init Camera position 初始化 摄像机 位置
if (enableCamera && cameraMode == CameraMode.SmoothFollow){
if (cameraTransform && cameraLookAt){
cameraTransform.position = cameraLookAt.TransformPoint( new Vector3(0,followHeight,-followDistance));
cameraTransform.LookAt( cameraLookAt);
}
}
if (enableCamera && cameraMode == CameraMode.Follow){
if (cameraTransform && cameraLookAt){
cameraTransform.position = cameraLookAt.position + followOffset;
cameraTransform.LookAt( cameraLookAt.position);
}
}
}
public override void Update (){
base.Update ();
#region dynamic joystick
if (joystickType == JoystickType.Dynamic && !_visible && _activated){
Vector2 localPosition = Vector2.zero;
Vector2 screenPosition = Vector2.zero;
//如果 触碰到 移动摇杆的区域
if (isTouchOverJoystickArea(ref localPosition, ref screenPosition)){
//获取第一个 选到的 UI 元素
GameObject overGO = GetFirstUIElement( screenPosition);
//如果是 覆盖到TouchPad 并且有挂载 ETCTouchPad,并且 在 区域上
if (overGO == null || (allowJoystickOverTouchPad && overGO.GetComponent()) || (overGO != null && overGO.GetComponent() ) ) {
//显示 移动摇杆,更新 位置
cachedRectTransform.anchoredPosition = localPosition;
visible = true;
}
}
}
if (joystickType == JoystickType.Dynamic && _visible){
if (GetTouchCount()==0){
visible = false;
}
}
#endregion
}
public override void LateUpdate (){
//初始化 摄像机看向 的方向
if (enableCamera && !cameraLookAt ){
InitCameraLookAt();
}
//找到摄像机,判断类型 设置跟随,更新 控制 状态
base.LateUpdate ();
}
//初始化 摄像机看向 的方向
private void InitCameraLookAt(){
if (cameraTargetMode == CameraTargetMode.FromDirectActionAxisX){
cameraLookAt = axisX.directTransform;
}
else if (cameraTargetMode == CameraTargetMode.FromDirectActionAxisY){
cameraLookAt = axisY.directTransform;
if (isTurnAndMove){
cameraLookAt = axisX.directTransform;
}
}
else if (cameraTargetMode == CameraTargetMode.LinkOnTag){
GameObject tmpobj = GameObject.FindGameObjectWithTag(camTargetTag);
if (tmpobj){
cameraLookAt = tmpobj.transform;
}
}
if (cameraLookAt)
cameraLookAtCC = cameraLookAt.GetComponent();
}
//重写 更新 控制 状态
protected override void UpdateControlState (){
//如果可见, 就更新虚拟摇杆信息, 位置,移动....
if (_visible){
UpdateJoystick();
}
else{
if (joystickType == JoystickType.Dynamic){
OnUp( false);
}
}
}
#endregion
#region UI Callback
public void OnPointerEnter(PointerEventData eventData){
if (joystickType == JoystickType.Dynamic && !isDynamicActif && _activated && pointId==-1){
eventData.pointerDrag = gameObject;
eventData.pointerPress = gameObject;
isDynamicActif = true;
pointId = eventData.pointerId;
}
if (joystickType == JoystickType.Dynamic && !eventData.eligibleForClick){
OnPointerUp( eventData );
}
}
public void OnPointerDown(PointerEventData eventData){
onTouchStart.Invoke();
pointId = eventData.pointerId;
if (isNoOffsetThumb){
OnDrag( eventData);
}
}
public void OnBeginDrag(PointerEventData eventData){
}
public void OnDrag(PointerEventData eventData){
if (pointId == eventData.pointerId){
isOnDrag = true;
isOnTouch = true;
float radius = GetRadius();
if (!isNoReturnThumb){
thumbPosition = (eventData.position - eventData.pressPosition);// / (cachedRootCanvas.rectTransform().localScale.x ) ;
}
else{
thumbPosition =((eventData.position - noReturnPosition) /cachedRootCanvas.rectTransform().localScale.x) + noReturnOffset;
}
if (isNoOffsetThumb){
thumbPosition = (eventData.position - (Vector2)cachedRectTransform.position) / cachedRootCanvas.rectTransform().localScale.x;
}
thumbPosition.x = Mathf.FloorToInt( thumbPosition.x);
thumbPosition.y = Mathf.FloorToInt( thumbPosition.y);
if (!axisX.enable){
thumbPosition.x=0;
}
if (!axisY.enable){
thumbPosition.y=0;
}
if (thumbPosition.magnitude > radius){
if (!isNoReturnThumb){
thumbPosition = thumbPosition.normalized * radius ;
}
else{
thumbPosition = thumbPosition.normalized * radius;
}
}
thumb.anchoredPosition = thumbPosition;
}
}
public void OnPointerUp (PointerEventData eventData){
if (pointId == eventData.pointerId){
OnUp();
}
}
private void OnUp(bool real=true){
isOnDrag = false;
isOnTouch = false;
if (isNoReturnThumb){
noReturnPosition = thumb.position;
noReturnOffset = thumbPosition;
}
if (!isNoReturnThumb){
thumbPosition = Vector2.zero;
thumb.anchoredPosition = Vector2.zero;
axisX.axisState = ETCAxis.AxisState.None;
axisY.axisState = ETCAxis.AxisState.None;
}
if (!axisX.isEnertia && !axisY.isEnertia){
axisX.ResetAxis();
axisY.ResetAxis();
tmpAxis = Vector2.zero;
OldTmpAxis = Vector2.zero;
if (real){
onMoveEnd.Invoke();
}
}
if (joystickType == JoystickType.Dynamic){
visible = false;
isDynamicActif = false;
}
pointId=-1;
if (real){
onTouchUp.Invoke();
}
}
#endregion
#region Joystick Update
protected override void DoActionBeforeEndOfFrame (){
axisX.DoGravity();
axisY.DoGravity();
}
//更新 虚拟摇杆 信息
private void UpdateJoystick(){
#region Unity axes
//如果允许 按键模拟,并且 没有在触摸, 可用,可见 更新位置信息
if (enableKeySimulation && !isOnTouch && _activated && _visible ){
float x = Input.GetAxis(axisX.unityAxis);
float y= Input.GetAxis(axisY.unityAxis);
if (!isNoReturnThumb){
thumb.localPosition = Vector2.zero;
}
isOnDrag = false;
//如果水平距离 大于0 ,则判断为 Drag
if (x!=0){
isOnDrag = true;
//更新 拇指的 局部信息, 获取半径再*x
thumb.localPosition = new Vector2(GetRadius()*x, thumb.localPosition.y);
}
//如果垂直距离 大于0 ,则判断为 Drag
if (y!=0){
isOnDrag = true;
//更新 拇指的 局部信息, 获取半径再*y
thumb.localPosition = new Vector2(thumb.localPosition.x,GetRadius()*y );
}
//保存 局部位置
thumbPosition = thumb.localPosition;
}
#endregion
// Computejoystick value 计算 虚拟 摇杆 值
//存上一针的 值
OldTmpAxis.x = axisX.axisValue;
OldTmpAxis.y = axisY.axisValue;
//Tmp轴 计算为 拇指的位置除以半径
tmpAxis = thumbPosition / GetRadius();
//更新 轴
axisX.UpdateAxis( tmpAxis.x,isOnDrag, ETCBase.ControlType.Joystick,true);
axisY.UpdateAxis( tmpAxis.y,isOnDrag, ETCBase.ControlType.Joystick,true);
//移动 事件
#region Move event
//如果x y 值 不为0 并且上次 轴 不为0
if ((axisX.axisValue!=0 || axisY.axisValue!=0 ) && OldTmpAxis == Vector2.zero){
//调用 移动开始的 事件
onMoveStart.Invoke();
}
if (axisX.axisValue!=0 || axisY.axisValue!=0 ){
//如果不允许 移动和旋转
if (!isTurnAndMove){
// 如果 按下,并且状态是左或者是右
if (axisX.actionOn == ETCAxis.ActionOn.Down && (axisX.axisState == ETCAxis.AxisState.DownLeft || axisX.axisState == ETCAxis.AxisState.DownRight))
{
//做 方向 运动
axisX.DoDirectAction();
}
//x轴 按下
else if (axisX.actionOn == ETCAxis.ActionOn.Press){
axisX.DoDirectAction();
}
// Y axis
if( axisY.actionOn == ETCAxis.ActionOn.Down && (axisY.axisState == ETCAxis.AxisState.DownUp || axisY.axisState == ETCAxis.AxisState.DownDown)){
axisY.DoDirectAction();
}//Y 轴 按下
else if (axisY.actionOn == ETCAxis.ActionOn.Press){
axisY.DoDirectAction();
}
}
else{
//否则 旋转和移动
DoTurnAndMove();
}
//调用 在移动的时候 的 事件
onMove.Invoke( new Vector2(axisX.axisValue,axisY.axisValue));
//调用移动 速度的 事件
onMoveSpeed.Invoke( new Vector2(axisX.axisSpeedValue,axisY.axisSpeedValue));
}
else if (axisX.axisValue==0 && axisY.axisValue==0 && OldTmpAxis!=Vector2.zero) {
//调用 移动结束 的事件
onMoveEnd.Invoke();
}
if (!isTurnAndMove){
if (axisX.axisValue==0 && axisX.directCharacterController ){
if (!axisX.directCharacterController.isGrounded && axisX.isLockinJump)
axisX.DoDirectAction();
}
if (axisY.axisValue==0 && axisY.directCharacterController ){
if (!axisY.directCharacterController.isGrounded && axisY.isLockinJump)
axisY.DoDirectAction();
}
}
else{ //! tmLock
if ((axisX.axisValue==0 && axisY.axisValue==0) && axisX.directCharacterController ){
if (!axisX.directCharacterController.isGrounded )
{
if (!tmLockInJump)
{
DoTurnAndMove();
}
else
{
tmLastMove = Vector3.zero;
}
}
}
}
#endregion
#region Down & press event
float coef =1;
if (axisX.invertedAxis) coef = -1;
if (Mathf.Abs(OldTmpAxis.x)< axisX.axisThreshold && Mathf.Abs(axisX.axisValue)>=axisX.axisThreshold){
if (axisX.axisValue*coef >0){
axisX.axisState = ETCAxis.AxisState.DownRight;
OnDownRight.Invoke();
}
else if (axisX.axisValue*coef<0){
axisX.axisState = ETCAxis.AxisState.DownLeft;
OnDownLeft.Invoke();
}
else{
axisX.axisState = ETCAxis.AxisState.None;
}
}
else if (axisX.axisState!= ETCAxis.AxisState.None) {
if (axisX.axisValue*coef>0){
axisX.axisState = ETCAxis.AxisState.PressRight;
OnPressRight.Invoke();
}
else if (axisX.axisValue*coef<0){
axisX.axisState = ETCAxis.AxisState.PressLeft;
OnPressLeft.Invoke();
}
else{
axisX.axisState = ETCAxis.AxisState.None;
}
}
coef =1;
if (axisY.invertedAxis) coef = -1;
if (Mathf.Abs(OldTmpAxis.y)< axisY.axisThreshold && Mathf.Abs(axisY.axisValue)>=axisY.axisThreshold ){
if (axisY.axisValue*coef>0){
axisY.axisState = ETCAxis.AxisState.DownUp;
OnDownUp.Invoke();
}
else if (axisY.axisValue*coef<0){
axisY.axisState = ETCAxis.AxisState.DownDown;
OnDownDown.Invoke();
}
else{
axisY.axisState = ETCAxis.AxisState.None;
}
}
else if (axisY.axisState!= ETCAxis.AxisState.None) {
if (axisY.axisValue*coef>0){
axisY.axisState = ETCAxis.AxisState.PressUp;
OnPressUp.Invoke();
}
else if (axisY.axisValue*coef<0){
axisY.axisState = ETCAxis.AxisState.PressDown;
OnPressDown.Invoke();
}
else{
axisY.axisState = ETCAxis.AxisState.None;
}
}
#endregion
}
#endregion
//是否 触摸 覆盖了 虚拟摇杆区域
#region Touch manager
private bool isTouchOverJoystickArea(ref Vector2 localPosition, ref Vector2 screenPosition){
bool touchOverArea = false;
bool doTest = false;
screenPosition = Vector2.zero;
int count = GetTouchCount();
int i=0;
//获取 触摸 计数, 遍历
while (i0){
returnValue = true;
}
break;
case JoystickArea.FullScreen: //全屏 覆盖true
returnValue = true;
break;
case JoystickArea.TopLeft:
if (localPosition.y>0 && localPosition.x<0){ // 左上, Y>0 X<0
returnValue = true;
}
break;
case JoystickArea.Top:
if (localPosition.y>0){ //上Y>0
returnValue = true;
}
break;
case JoystickArea.TopRight: //右上 y>0 X>0
if (localPosition.y>0 && localPosition.x>0){
returnValue=true;
}
break;
case JoystickArea.BottomLeft: //左下 Y<0 X<0
if (localPosition.y<0 && localPosition.x<0){
returnValue = true;
}
break;
case JoystickArea.Bottom: //下 y<0
if (localPosition.y<0){
returnValue = true;
}
break;
case JoystickArea.BottomRight: //右下 y<0 x>0
if (localPosition.y<0 && localPosition.x>0){
returnValue = true;
}
break;
}
}
}
else
{ // 从指定摄像机中观看该矩形变换是否包含屏幕点?
if (RectTransformUtility.RectangleContainsScreenPoint( userArea, screenPosition, cachedRootCanvas.worldCamera )){//再将屏幕位置 转换为UI 坐标
RectTransformUtility.ScreenPointToLocalPointInRectangle( cachedRootCanvas.rectTransform(),screenPosition,cachedRootCanvas.worldCamera,out localPosition);
returnValue = true;
}
}
return returnValue;
}
//获取 触摸的计数
private int GetTouchCount(){
#if ((UNITY_ANDROID || UNITY_IOS || UNITY_WINRT || UNITY_BLACKBERRY) && !UNITY_EDITOR)
return Input.touchCount;
#else
if (Input.GetMouseButton(0) || Input.GetMouseButtonUp(0)){//左键或者右键返回1
return 1;
}
else{
return 0;
}
#endif
}
#endregion
#region Other private method
//半径计算 方式 获取半径
public float GetRadius(){
float radius =0;
switch (radiusBase){
case RadiusBase.Width:
radius = cachedRectTransform.sizeDelta.x * 0.5f;
break;
case RadiusBase.Height:
radius = cachedRectTransform.sizeDelta.y * 0.5f;
break;
case RadiusBase.UserDefined:
radius = radiusBaseValue;
break;
}
return radius;
}
//设置 是否 启用
protected override void SetActivated (){
GetComponent().blocksRaycasts = _activated;
if (!_activated){
OnUp(false);
}
}
//设置 是否 可见 默认可见
protected override void SetVisible (bool visible=true){
bool localVisible = _visible;
if (!visible){
localVisible = visible;
}
GetComponent
().enabled = localVisible;
thumb.GetComponent
().enabled = localVisible;
GetComponent().blocksRaycasts = _activated;
}
#endregion
//移动 和 旋转 的 计算 方法
private void DoTurnAndMove(){
//通过反正切 获取角度 返回是弧度, 再乘以57.2957
float angle =Mathf.Atan2( axisX.axisValue,axisY.axisValue ) * Mathf.Rad2Deg;
// 从 tm移动曲线 中取得 二维向量的长度 再 乘以tm速度
float speed = tmMoveCurve.Evaluate( new Vector2(axisX.axisValue,axisY.axisValue).magnitude) * tmSpeed;
if (axisX.directTransform != null){
axisX.directTransform.rotation = Quaternion.Euler(new Vector3(0, angle + tmAdditionnalRotation,0));
if (axisX.directCharacterController != null){
if (axisX.directCharacterController.isGrounded || !tmLockInJump){
// 世界的 CC向前的方向*speed 得到move 向量
Vector3 move = axisX.directCharacterController.transform.TransformDirection(Vector3.forward) * speed;
//移动
axisX.directCharacterController.Move(move* Time.deltaTime);
}
else{
// 如果锁跳, 就用tmLastMove*Time.delTime
axisX.directCharacterController.Move(tmLastMove* Time.deltaTime);
}
}
else{// 局部的 向前 位移
axisX.directTransform.Translate(Vector3.forward * speed * Time.deltaTime,Space.Self);
}
}
}
// 初始 化 曲线,
public void InitCurve(){
axisX.InitDeadCurve();
axisY.InitDeadCurve();
InitTurnMoveCurve();
}
//初始 化 旋转 移动曲线
public void InitTurnMoveCurve(){
tmMoveCurve = AnimationCurve.EaseInOut(0,0,1,1);
tmMoveCurve.postWrapMode = WrapMode.PingPong;
tmMoveCurve.preWrapMode = WrapMode.PingPong;
}
}