首页 > 技术文章 > Complete Physics Platformer Kit 学习

revoid 2017-03-20 23:39 原文

  1 using UnityEngine;
  2 
  3 /// <summary>
  4 /// 摄像机跟随玩家
  5 /// </summary>
  6 public class CameraFollow : MonoBehaviour 
  7 {
  8     /// <summary>
  9     /// 目标
 10     /// </summary>
 11     public Transform target;                                    
 12     /// <summary>
 13     /// 和目标之间的偏移
 14     /// </summary>
 15     public Vector3 targetOffset =  new Vector3(0f, 3.5f, 7);    
 16     /// <summary>
 17     /// 是否锁定旋转
 18     /// </summary>
 19     public bool lockRotation;                                   
 20     /// <summary>
 21     /// 跟随速度
 22     /// </summary>
 23     public float followSpeed = 6;                               
 24     /// <summary>
 25     /// 围绕目标旋转的速度
 26     /// </summary>
 27     public float inputRotationSpeed = 100;                      
 28     /// <summary>
 29     /// 是否可以由鼠标控制旋转(只有在相机没有被固定的情况下)
 30     /// </summary>
 31     public bool mouseFreelook;                                    
 32     /// <summary>
 33     /// 旋转阻尼
 34     /// </summary>
 35     public float rotateDamping = 100;                           
 36     /// <summary>
 37     /// 摄像机在水中时的过滤物体
 38     /// </summary>
 39     public GameObject waterFilter;                              
 40     /// <summary>
 41     /// 
 42     /// </summary>
 43     public string[] avoidClippingTags;                                      
 44     
 45     /// <summary>
 46     /// Camera Target
 47     /// </summary>
 48     private Transform followTarget;
 49     /// <summary>
 50     /// 暂未用到
 51     /// </summary>
 52     private bool camColliding;
 53     
 54     //setup objects
 55     void Awake()
 56     {
 57         //创建跟随物体(Camera Target)
 58         followTarget = new GameObject().transform;
 59         followTarget.name = "Camera Target";
 60         if(waterFilter)
 61             waterFilter.GetComponent<Renderer>().enabled = false;
 62         if(!target)
 63             Debug.LogError("'CameraFollow script' has no target assigned to it", transform);
 64         
 65         //如果用鼠标控制旋转,旋转阻尼为0
 66         if(mouseFreelook)
 67             rotateDamping = 0f;
 68     }
 69     
 70     void Update()
 71     {
 72         //没有目标不处理
 73         if (!target)
 74             return;
 75         //平滑跟随
 76         SmoothFollow ();
 77         //平滑朝向玩家
 78         if(rotateDamping > 0)
 79             SmoothLookAt();
 80         //直接朝向玩家
 81         else
 82             transform.LookAt(target.position);
 83     }
 84 
 85 
 86     void OnTriggerEnter(Collider other)
 87     {
 88         //打开水遮罩
 89         if (other.tag == "Water" && waterFilter)
 90             waterFilter.GetComponent<Renderer>().enabled = true;
 91     }
 92     
 93     void OnTriggerExit(Collider other)
 94     {
 95         //关闭水遮罩
 96         if (other.tag == "Water" && waterFilter)
 97             waterFilter.GetComponent<Renderer>().enabled = false;
 98     }
 99     
100     /// <summary>
101     /// 平滑朝向目标
102     /// </summary>
103     void SmoothLookAt()
104     {
105         Quaternion rotation = Quaternion.LookRotation (target.position - transform.position);
106         transform.rotation = Quaternion.Slerp (transform.rotation, rotation, rotateDamping * Time.deltaTime);
107     }
108         
109     /// <summary>
110     /// 平滑跟随目标
111     /// </summary>
112     void SmoothFollow()
113     {
114         followTarget.position = target.position;
115         followTarget.Translate(targetOffset, Space.Self);
116         if (lockRotation)
117             followTarget.rotation = target.rotation;
118         
119         //鼠标控制围绕目标旋转
120         if(mouseFreelook)
121         {
122             float axisX = Input.GetAxis ("Mouse X") * inputRotationSpeed * Time.deltaTime;
123             followTarget.RotateAround (target.position,Vector3.up, axisX);
124             float axisY = Input.GetAxis ("Mouse Y") * inputRotationSpeed * Time.deltaTime;
125             followTarget.RotateAround (target.position, transform.right, -axisY);
126         }
127         //键盘控制围绕目标旋转
128         else
129         {
130             float axis = Input.GetAxis ("CamHorizontal") * inputRotationSpeed * Time.deltaTime;
131             followTarget.RotateAround (target.position, Vector3.up, axis);
132         }
133         
134         //计算相机的下一帧位置
135         Vector3 nextFramePosition = Vector3.Lerp(transform.position, followTarget.position, followSpeed * Time.deltaTime);
136         Vector3 direction = nextFramePosition - target.position;
137         //射线检测这个位置
138         RaycastHit hit;
139         if(Physics.Raycast (target.position, direction, out hit, direction.magnitude + 0.3f))
140         {
141             transform.position = nextFramePosition;
142             foreach(string tag in avoidClippingTags)
143                 //如果这个位置有物体,修改相机位置
144                 if(hit.transform.tag == tag)
145                     transform.position = hit.point - direction.normalized * 0.3f;
146         }
147         else
148         {
149             //直接修改相机位置
150             transform.position = nextFramePosition;
151         }
152     }
153 }
CameraFollow
  1 using UnityEngine;
  2 
  3 /// <summary>
  4 /// 角色移动
  5 /// </summary>
  6 [RequireComponent(typeof(Rigidbody))]
  7 public class CharacterMotor:MonoBehaviour {
  8     /// <summary>
  9     /// 是否冻结z轴的移动
 10     /// </summary>
 11     public bool sidescroller;
 12     /// <summary>
 13     /// 当前速度
 14     /// </summary>
 15     [HideInInspector]
 16     public Vector3 currentSpeed;
 17     /// <summary>
 18     /// 到目标的距离
 19     /// </summary>
 20     [HideInInspector]
 21     public float DistanceToTarget;
 22     /// <summary>
 23     /// 刚体
 24     /// </summary>
 25     private Rigidbody rigid;
 26 
 27     void Awake() {
 28         //设置rigidbody
 29         rigid = GetComponent<Rigidbody>();
 30         rigid.interpolation = RigidbodyInterpolation.Interpolate;
 31         if(sidescroller)
 32             rigid.constraints = RigidbodyConstraints.FreezeRotation | RigidbodyConstraints.FreezePositionZ;
 33         else
 34             rigid.constraints = RigidbodyConstraints.FreezeRotation;
 35         //添加光滑的物理材质
 36         if(GetComponent<Collider>().material.name == "Default (Instance)") {
 37             PhysicMaterial pMat = new PhysicMaterial();
 38             pMat.name = "Frictionless";
 39             pMat.frictionCombine = PhysicMaterialCombine.Multiply;
 40             pMat.bounceCombine = PhysicMaterialCombine.Multiply;
 41             pMat.dynamicFriction = 0f;
 42             pMat.staticFriction = 0f;
 43             GetComponent<Collider>().material = pMat;
 44             Debug.LogWarning("No physics material found for CharacterMotor, a frictionless one has been created and assigned",transform);
 45         }
 46     }
 47 
 48     /// <summary>
 49     /// 移动刚体到指定位置
 50     /// </summary>
 51     public bool MoveTo(Vector3 destination,float acceleration,float stopDistance,bool ignoreY) {
 52         Vector3 relativePos = (destination - transform.position);
 53         //忽略y轴?
 54         if(ignoreY)
 55             relativePos.y = 0;
 56 
 57         DistanceToTarget = relativePos.magnitude;
 58         if(DistanceToTarget <= stopDistance)
 59             return true;
 60         else
 61             rigid.AddForce(relativePos.normalized * acceleration * Time.deltaTime,ForceMode.VelocityChange);
 62         return false;
 63     }
 64 
 65     /// <summary>
 66     /// 旋转刚体朝向当前速度
 67     /// </summary>
 68     public void RotateToVelocity(float turnSpeed,bool ignoreY) {
 69         Vector3 dir;
 70         if(ignoreY)
 71             dir = new Vector3(rigid.velocity.x,0f,rigid.velocity.z);
 72         else
 73             dir = rigid.velocity;
 74 
 75         if(dir.magnitude > 0.1) {
 76             Quaternion dirQ = Quaternion.LookRotation(dir);
 77             Quaternion slerp = Quaternion.Slerp(transform.rotation,dirQ,dir.magnitude * turnSpeed * Time.deltaTime);
 78             rigid.MoveRotation(slerp);
 79         }
 80     }
 81 
 82     /// <summary>
 83     /// 旋转刚体朝向指定方向
 84     /// </summary>
 85     public void RotateToDirection(Vector3 lookDir,float turnSpeed,bool ignoreY) {
 86         Vector3 characterPos = transform.position;
 87         //忽略y轴?
 88         if(ignoreY) {
 89             characterPos.y = 0;
 90             lookDir.y = 0;
 91         }
 92 
 93         Vector3 newDir = lookDir - characterPos;
 94         Quaternion dirQ = Quaternion.LookRotation(newDir);
 95         Quaternion slerp = Quaternion.Slerp(transform.rotation,dirQ,turnSpeed * Time.deltaTime);
 96         rigid.MoveRotation(slerp);
 97     }
 98 
 99     /// <summary>
100     /// 管理速度
101     /// </summary>
102     public void ManageSpeed(float deceleration,float maxSpeed,bool ignoreY) {
103         currentSpeed = rigid.velocity;
104         //忽略y轴
105         if(ignoreY)
106             currentSpeed.y = 0;
107 
108         if(currentSpeed.magnitude > 0) {
109             rigid.AddForce((currentSpeed * -1) * deceleration * Time.deltaTime,ForceMode.VelocityChange);
110             if(rigid.velocity.magnitude > maxSpeed)
111                 rigid.AddForce((currentSpeed * -1) * deceleration * Time.deltaTime,ForceMode.VelocityChange);
112         }
113     }
114 }
CharacterMotor
 1 using UnityEngine;
 2 
 3 /// <summary>
 4 /// 检查点
 5 /// </summary>
 6 [RequireComponent(typeof(CapsuleCollider))]
 7 [RequireComponent(typeof(AudioSource))]
 8 public class Checkpoint : MonoBehaviour 
 9 {
10     /// <summary>
11     /// 激活颜色
12     /// </summary>
13     public Color activeColor = Color.green;    
14     /// <summary>
15     /// 激活时的不透明度
16     /// </summary>
17     public float activeColorOpacity = 0.4f;          
18     
19     /// <summary>
20     /// 生命
21     /// </summary>
22     private Health health;
23     /// <summary>
24     /// 默认颜色
25     /// </summary>
26     private Color defColor;
27     /// <summary>
28     /// 检查点列表
29     /// </summary>
30     private GameObject[] checkpoints;
31     /// <summary>
32     /// 渲染器
33     /// </summary>
34     private Renderer render;
35     /// <summary>
36     /// AudioSource
37     /// </summary>
38     private AudioSource aSource;
39 
40     void Awake()
41     {
42         render = GetComponent<Renderer>();
43         aSource = GetComponent<AudioSource>();
44         //标签不是Respawn,自动修改标签
45         if(tag != "Respawn")
46         {
47             tag = "Respawn";
48             Debug.LogWarning ("'Checkpoint' script attached to object without the 'Respawn' tag, tag has been assigned automatically", transform);    
49         }
50         GetComponent<Collider>().isTrigger = true;
51         //设置默认颜色和不透明度
52         if(render)
53             defColor = render.material.color;
54         activeColor.a = activeColorOpacity;
55     }
56     
57 
58     void Start()
59     {
60         checkpoints = GameObject.FindGameObjectsWithTag("Respawn");
61         health = GameObject.FindGameObjectWithTag("Player").GetComponent<Health>();
62         if(!health)
63             Debug.LogError("For Checkpoint to work, the Player needs 'Health' script attached", transform);
64     }
65     
66     
67     void OnTriggerEnter(Collider other)
68     {
69         //如果碰到玩家
70         if(other.transform.tag == "Player" && health)
71         {
72             health.respawnPos = transform.position;
73             
74             if(render.material.color != activeColor)
75             {
76                 foreach (GameObject checkpoint in checkpoints)
77                     checkpoint.GetComponent<Renderer>().material.color = defColor;
78                 aSource.Play();
79                 render.material.color = activeColor;
80             }
81         }
82     }
83 }
Checkpoint
  1 using UnityEngine;
  2 
  3 /// <summary>
  4 /// 金币
  5 /// </summary>
  6 [RequireComponent(typeof(SphereCollider))]
  7 public class Coin : MonoBehaviour 
  8 {
  9     /// <summary>
 10     /// 被收集时的声音
 11     /// </summary>
 12     public AudioClip collectSound;                            
 13     /// <summary>
 14     /// 旋转
 15     /// </summary>
 16     public Vector3 rotation = new Vector3(0, 80, 0);        
 17     /// <summary>
 18     /// 玩家靠近时,增加的旋转
 19     /// </summary>
 20     public Vector3 rotationGain = new Vector3(10, 20, 10);    
 21     /// <summary>
 22     /// 金币向玩家移动的速度
 23     /// </summary>
 24     public float startSpeed = 3f;                            
 25     /// <summary>
 26     /// 金币向玩家移动的加速度
 27     /// </summary>
 28     public float speedGain = 0.2f;                             
 29     
 30     /// <summary>
 31     /// 是否被搜集
 32     /// </summary>
 33     private bool collected;
 34     /// <summary>
 35     /// 玩家
 36     /// </summary>
 37     private Transform player;
 38     /// <summary>
 39     /// 子物体的触发器
 40     /// </summary>
 41     private TriggerParent triggerParent;
 42     /// <summary>
 43     /// GUI
 44     /// </summary>
 45     private GUIManager gui;
 46     
 47 
 48     void Awake()
 49     {
 50         gui = FindObjectOfType(typeof(GUIManager)) as GUIManager ;
 51         if(tag != "Coin")
 52         {
 53             tag = "Coin";
 54             Debug.LogWarning ("'Coin' script attached to object not tagged 'Coin', tag added automatically", transform);
 55         }
 56         GetComponent<Collider>().isTrigger = true;
 57         triggerParent = GetComponentInChildren<TriggerParent>();
 58         //添加bounds子物体
 59         if(!triggerParent)
 60         {
 61             GameObject bounds = new GameObject();
 62             bounds.name = "Bounds";
 63             bounds.AddComponent<SphereCollider>();
 64             bounds.GetComponent<SphereCollider>().radius = 7f;
 65             bounds.GetComponent<SphereCollider>().isTrigger = true;
 66             bounds.transform.parent = transform;
 67             bounds.transform.position = transform.position;
 68             bounds.AddComponent<TriggerParent>();
 69             triggerParent = GetComponentInChildren<TriggerParent>();
 70             triggerParent.tagsToCheck = new string[1];
 71             triggerParent.tagsToCheck[0] = "Player";
 72             Debug.LogWarning ("No pickup radius 'bounds' trigger attached to coin: " + transform.name + ", one has been added automatically", bounds);
 73         }
 74     }
 75     
 76     void Start()
 77     {
 78         player = GameObject.FindGameObjectWithTag("Player").transform;
 79     }
 80 
 81     void Update()
 82     {
 83 
 84         transform.Rotate (rotation * Time.deltaTime, Space.World);
 85         
 86         if(triggerParent.collided)
 87             collected = true;
 88         
 89         //增加金币的旋转速度和移动速度,将金币移向玩家
 90         if (collected)
 91         {
 92             startSpeed += speedGain;
 93             rotation += rotationGain;
 94             transform.position = Vector3.Lerp (transform.position, player.position, startSpeed * Time.deltaTime);
 95         }    
 96     }
 97     
 98     void OnTriggerEnter(Collider other)
 99     {
100         if (other.tag == "Player")
101             CoinGet();
102     }
103     
104     /// <summary>
105     /// 获取金币
106     /// </summary>
107     void CoinGet()
108     {
109         if(collectSound)
110             AudioSource.PlayClipAtPoint(collectSound, transform.position);
111         if (gui)
112             gui.coinsCollected ++;
113         Destroy(gameObject);
114     }
115 }
Coin
 1 using UnityEngine;
 2 
 3 /// <summary>
 4 /// 处理伤害
 5 /// </summary>
 6 public class DealDamage : MonoBehaviour 
 7 {
 8     /// <summary>
 9     /// 受害人的生命
10     /// </summary>
11     private Health health;
12     
13     /// <summary>
14     /// 攻击
15     /// </summary>
16     public void Attack(GameObject victim, int dmg, float pushHeight, float pushForce)
17     {
18         health = victim.GetComponent<Health>();        
19         
20         //推物体
21         Vector3 pushDir = (victim.transform.position - transform.position);
22         pushDir.y = 0f;
23         pushDir.y = pushHeight * 0.1f;
24         if (victim.GetComponent<Rigidbody>() && !victim.GetComponent<Rigidbody>().isKinematic)
25         {
26             victim.GetComponent<Rigidbody>().velocity = new Vector3(0, 0, 0);
27             victim.GetComponent<Rigidbody>().AddForce (pushDir.normalized * pushForce, ForceMode.VelocityChange);
28             victim.GetComponent<Rigidbody>().AddForce (Vector3.up * pushHeight, ForceMode.VelocityChange);
29         }
30         //应用伤害
31         if(health && !health.flashing)
32             health.currentHealth -= dmg;
33     }
34 }
DealDamage
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 /// <summary>
 5 /// 摧毁物体
 6 /// </summary>
 7 public class DestroyObject : MonoBehaviour 
 8 {
 9     /// <summary>
10     /// 摧毁声音
11     /// </summary>
12     public AudioClip destroySound;    
13     /// <summary>
14     /// 延迟
15     /// </summary>
16     public float delay;                               
17     /// <summary>
18     /// 是否分离子物体并且不摧毁子物体
19     /// </summary>
20     public bool destroyChildren;    
21     /// <summary>
22     /// 从父物体中心推开子物体的数量
23     /// </summary>
24     public float pushChildAmount;    
25     
26     
27     void Start()
28     {
29         //获取子物体列表
30         Transform[] children = new Transform[transform.childCount];
31         for (int i = 0; i < transform.childCount; i++)
32             children[i] = transform.GetChild(i);
33         
34         //分离子物体
35         if (!destroyChildren)
36             transform.DetachChildren();
37         
38         //给子物体添加一个推力和旋转
39         foreach (Transform child in children)
40         {
41             Rigidbody rigid = child.GetComponent<Rigidbody>();
42             if(rigid && pushChildAmount != 0)
43             {
44                 Vector3 pushDir = child.position - transform.position;
45                 rigid.AddForce(pushDir * pushChildAmount, ForceMode.Force);
46                 rigid.AddTorque(Random.insideUnitSphere, ForceMode.Force);
47             }
48         }
49         
50         //删除父物体
51         if(destroySound)
52             AudioSource.PlayClipAtPoint(destroySound, transform.position);
53         Destroy (gameObject, delay);
54     }
55 }
DestroyObject
  1 using UnityEngine;
  2 using System.Collections;
  3 
  4 /// <summary>
  5 /// 敌人AI
  6 /// </summary>
  7 [RequireComponent(typeof(CharacterMotor))]
  8 [RequireComponent(typeof(DealDamage))]
  9 public class EnemyAI : MonoBehaviour 
 10 {
 11     /// <summary>
 12     /// 移动加速度
 13     /// </summary>
 14     public float acceleration = 35f;                    
 15     /// <summary>
 16     /// 移动减速度
 17     /// </summary>
 18     public float deceleration = 8f;                        
 19     /// <summary>
 20     /// 旋转速度
 21     /// </summary>
 22     public float rotateSpeed = 0.7f;                    
 23     /// <summary>
 24     /// 速度极限
 25     /// </summary>
 26     public float speedLimit = 10f;                        
 27     /// <summary>
 28     /// 玩家跳到敌人头上时应用到玩家身上的力
 29     /// </summary>
 30     public Vector3 bounceForce = new Vector3(0, 13, 0);    
 31     /// <summary>
 32     /// 玩家跳到敌人头上时的声音
 33     /// </summary>
 34     public AudioClip bounceSound;                        
 35     /// <summary>
 36     /// 玩家碰到敌人时受到的推力
 37     /// </summary>
 38     public float pushForce = 10f;                       
 39     /// <summary>
 40     /// 玩家碰到敌人时玩家的高度
 41     /// </summary>
 42     public float pushHeight = 7f;                       
 43     /// <summary>
 44     /// 敌人对玩家的伤害
 45     /// </summary>
 46     public int attackDmg = 1;                            
 47     /// <summary>
 48     /// 是否追逐视野内的目标
 49     /// </summary>
 50     public bool chase = true;                           
 51     /// <summary>
 52     /// 追逐时是否忽略y轴
 53     /// </summary>
 54     public bool ignoreY = true;                            
 55     /// <summary>
 56     /// 追逐停止的距离
 57     /// </summary>
 58     public float chaseStopDistance = 0.7f;                
 59     /// <summary>
 60     /// 视野范围
 61     /// </summary>
 62     public GameObject sightBounds;                        
 63     /// <summary>
 64     /// 攻击范围
 65     /// </summary>
 66     public GameObject attackBounds;                        
 67     /// <summary>
 68     /// Animator
 69     /// </summary>
 70     public Animator animatorController;                    
 71     /// <summary>
 72     /// 移动到路标点
 73     /// </summary>
 74     public MoveToPoints moveToPointsScript;                                          
 75     
 76     /// <summary>
 77     /// 视野触发器
 78     /// </summary>
 79     private TriggerParent sightTrigger;
 80     /// <summary>
 81     /// 攻击触发器
 82     /// </summary>
 83     private TriggerParent attackTrigger;
 84     /// <summary>
 85     ///  玩家移动
 86     /// </summary>
 87     private PlayerMove playerMove;
 88     /// <summary>
 89     /// 角色移动
 90     /// </summary>
 91     private CharacterMotor characterMotor;
 92     /// <summary>
 93     /// 处理伤害
 94     /// </summary>
 95     private DealDamage dealDamage;
 96     
 97     
 98     void Awake()
 99     {        
100         characterMotor = GetComponent<CharacterMotor>();
101         dealDamage = GetComponent<DealDamage>();
102         if(tag != "Enemy")
103         {
104             tag = "Enemy";
105             Debug.LogWarning("'EnemyAI' script attached to object without 'Enemy' tag, it has been assign automatically", transform);
106         }
107         
108         if(sightBounds)
109         {
110             sightTrigger = sightBounds.GetComponent<TriggerParent>();
111             if(!sightTrigger)
112                 Debug.LogError("'TriggerParent' script needs attaching to enemy 'SightBounds'", sightBounds);
113         }
114         if(!sightBounds)
115             Debug.LogWarning("Assign a trigger with 'TriggerParent' script attached, to 'SightBounds' or enemy will not be able to see", transform);
116         
117         if(attackBounds)
118         {
119             attackTrigger = attackBounds.GetComponent<TriggerParent>();
120             if(!attackTrigger)
121                 Debug.LogError("'TriggerParent' script needs attaching to enemy 'attackBounds'", attackBounds);
122         }
123         else
124             Debug.LogWarning("Assign a trigger with 'TriggerParent' script attached, to 'AttackBounds' or enemy will not be able to attack", transform);
125     }
126     
127     void Update()
128     {
129         //追逐
130         if (sightTrigger && sightTrigger.colliding && chase && sightTrigger.hitObject != null && sightTrigger.hitObject.activeInHierarchy)
131         {
132             characterMotor.MoveTo (sightTrigger.hitObject.transform.position, acceleration, chaseStopDistance, ignoreY);
133 
134             if(animatorController)
135                 animatorController.SetBool("Moving", true);
136 
137             if(moveToPointsScript)
138                 moveToPointsScript.enabled = false;
139         }
140         else
141         {    
142 
143             if(animatorController)
144                 animatorController.SetBool("Moving", false);
145 
146             if(moveToPointsScript)
147                 moveToPointsScript.enabled = true;
148         }
149         
150         //攻击
151         if (attackTrigger && attackTrigger.collided)
152         {
153             dealDamage.Attack(attackTrigger.hitObject, attackDmg, pushHeight, pushForce);
154 
155             if(animatorController)
156                 animatorController.SetBool("Attacking", true);    
157         }
158         else if(animatorController)
159             animatorController.SetBool("Attacking", false);
160     }
161     
162     void FixedUpdate()
163     {
164         characterMotor.ManageSpeed(deceleration, speedLimit, ignoreY);
165         characterMotor.RotateToVelocity (rotateSpeed, ignoreY);
166     }
167     
168     /// <summary>
169     /// 弹开玩家
170     /// </summary>
171     public void BouncedOn()
172     {    
173         if(!playerMove)
174             playerMove = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerMove>();
175         if (bounceSound)
176             AudioSource.PlayClipAtPoint(bounceSound, transform.position);
177         if(playerMove)
178         {
179             Vector3 bounceMultiplier = new Vector3(0f, 1.5f, 0f) * playerMove.onEnemyBounce;
180             playerMove.Jump (bounceForce + bounceMultiplier);
181         }
182         else
183             Debug.LogWarning("'Player' tagged object landed on enemy, but without playerMove script attached, is unable to bounce");
184     }
185 }
EnemyAI
 1 using UnityEngine;
 2 
 3 /// <summary>
 4 /// 关卡目标
 5 /// </summary>
 6 [RequireComponent(typeof(CapsuleCollider))]
 7 public class Goal : MonoBehaviour 
 8 {
 9     /// <summary>
10     /// 举起玩家的力
11     /// </summary>
12     public float lift;            
13     /// <summary>
14     /// 加载下一个关卡的等待时间
15     /// </summary>
16     public float loadDelay;        
17     /// <summary>
18     /// 下一个场景的索引
19     /// </summary>
20     public int nextLevelIndex;                                                                
21     
22     /// <summary>
23     /// 计时器
24     /// </summary>
25     private float counter;
26     
27     void Awake()
28     {
29         GetComponent<Collider>().isTrigger = true;
30     }
31     
32 
33     void OnTriggerStay(Collider other)
34     {
35         Rigidbody rigid = other.GetComponent<Rigidbody>();
36         if(rigid)
37             rigid.AddForce(Vector3.up * lift, ForceMode.Force);
38         
39         if (other.tag == "Player")
40         {
41             counter += Time.deltaTime;
42             if(counter > loadDelay)
43                 Application.LoadLevel (nextLevelIndex);
44         }
45     }
46     
47     void OnTriggerExit(Collider other)
48     {
49         if (other.tag == "Player")
50             counter = 0f;
51     }
52 }
Goal
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 /// <summary>
 5 /// GUI
 6 /// </summary>
 7 public class GUIManager : MonoBehaviour 
 8 {    
 9     /// <summary>
10     /// GUISkin
11     /// </summary>
12     public GUISkin guiSkin;
13     /// <summary>
14     /// 已收集的金币
15     /// </summary>
16     [HideInInspector]
17     public int coinsCollected;
18     /// <summary>
19     /// 场景中的金币数量
20     /// </summary>
21     private int coinsInLevel;
22     /// <summary>
23     /// 生命
24     /// </summary>
25     private Health health;
26     
27     void Start()
28     {
29         coinsInLevel = GameObject.FindGameObjectsWithTag("Coin").Length;        
30         health = GameObject.FindGameObjectWithTag("Player").GetComponent<Health>();
31     }
32     
33     void OnGUI()
34     {
35         GUI.skin = guiSkin;
36         GUILayout.Space(5f);
37         
38         if(health)
39             GUILayout.Label ("Health: " + health.currentHealth);
40         if(coinsInLevel > 0)
41             GUILayout.Label ("Cubes: " + coinsCollected + " / " + coinsInLevel);
42     }
43 }
GUIManager
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 /// <summary>
 5 /// 障碍
 6 /// </summary>
 7 [RequireComponent(typeof(DealDamage))]
 8 [RequireComponent(typeof(AudioSource))]
 9 public class Hazard : MonoBehaviour 
10 {
11     /// <summary>
12     /// 推开受害者的力
13     /// </summary>
14     public float pushForce = 25f;                            
15     /// <summary>
16     /// 向上推的力
17     /// </summary>
18     public float pushHeight = 6f;                           
19     /// <summary>
20     /// 照成的伤害
21     /// </summary>
22     public int damage = 1;                                  
23     /// <summary>
24     /// 是否是触发
25     /// </summary>
26     public bool triggerEnter;                               
27     /// <summary>
28     /// 是否是碰撞
29     /// </summary>
30     public bool collisionEnter = true;                                    
31     /// <summary>
32     /// 影响的单位的标签
33     /// </summary>
34     public string[] effectedTags = {"Player"};              
35     /// <summary>
36     /// 碰撞声音
37     /// </summary>
38     public AudioClip hitSound;                                      
39     
40     /// <summary>
41     ///  处理伤害
42     /// </summary>
43     private DealDamage dealDamage;
44     /// <summary>
45     /// AudioSource
46     /// </summary>
47     private AudioSource aSource;
48 
49     //setup
50     void Awake()
51     {
52         aSource = GetComponent<AudioSource>();
53         aSource.playOnAwake = false;
54         dealDamage = GetComponent<DealDamage>();
55     }
56     
57     void OnCollisionEnter(Collision col)
58     {
59         //不是碰撞
60         if(!collisionEnter)
61             return;
62         //遍历检查标签
63         foreach(string tag in effectedTags)
64             //标签相等
65             if(col.transform.tag == tag)
66             {
67                 //处理伤害
68                 dealDamage.Attack (col.gameObject, damage, pushHeight, pushForce);
69                 //播放声音
70                 if (hitSound)
71                 {
72                     aSource.clip = hitSound;
73                     aSource.Play();
74                 }
75             }
76     }
77     
78     void OnTriggerEnter(Collider other)
79     {
80         //不是触发器
81         if(!triggerEnter)
82             return;
83         //遍历标签
84         foreach(string tag in effectedTags)
85             //标签相等
86             if(other.transform.tag == tag)
87                 //处理伤害
88                 dealDamage.Attack (other.gameObject, damage, pushHeight, pushForce);
89     }
90 }
Hazard
  1 using UnityEngine;
  2 using System.Collections;
  3 
  4 /// <summary>
  5 /// 生命
  6 /// </summary>
  7 [RequireComponent(typeof(AudioSource))]
  8 public class Health : MonoBehaviour 
  9 {
 10     /// <summary>
 11     /// 撞击声音
 12     /// </summary>
 13     public AudioClip impactSound;                    
 14     /// <summary>
 15     /// 受到伤害时的声音
 16     /// </summary>
 17     public AudioClip hurtSound;                        
 18     /// <summary>
 19     /// 死亡声音
 20     /// </summary>
 21     public AudioClip deadSound;                        
 22     /// <summary>
 23     /// 当前生命
 24     /// </summary>
 25     public int currentHealth = 1;                    
 26     /// <summary>
 27     /// 是否能受到撞击伤害
 28     /// </summary>
 29     public bool takeImpactDmg;                        
 30     /// <summary>
 31     /// 是否只能受到刚体伤害
 32     /// </summary>
 33     public bool onlyRigidbodyImpact;                
 34     /// <summary>
 35     /// 是否可以重生
 36     /// </summary>
 37     public bool respawn;                            
 38     /// <summary>
 39     /// 不会受到撞击伤害的标签
 40     /// </summary>
 41     public string[] impactFilterTag;                
 42     /// <summary>
 43     /// 受到伤害时的闪烁延迟
 44     /// </summary>
 45     public float hitFlashDelay = 0.1f;                
 46     /// <summary>
 47     /// 闪烁持续时间
 48     /// </summary>
 49     public float flashDuration = 0.9f;                
 50     /// <summary>
 51     /// 受到伤害时的闪烁颜色
 52     /// </summary>
 53     public Color hitFlashColor = Color.red;            
 54     /// <summary>
 55     /// 闪烁的物体
 56     /// </summary>
 57     public Transform flashObject;                    
 58     /// <summary>
 59     /// 
 60     /// </summary>
 61     public GameObject[] spawnOnDeath;                
 62     
 63     /// <summary>
 64     /// 是否死亡,是否闪烁
 65     /// </summary>
 66     [HideInInspector]
 67     public bool dead, flashing;
 68     /// <summary>
 69     /// 重生位置
 70     /// </summary>
 71     [HideInInspector]
 72     public Vector3 respawnPos;
 73     
 74     /// <summary>
 75     /// 原始颜色
 76     /// </summary>
 77     private Color originalColor;
 78     /// <summary>
 79     /// 
 80     /// </summary>
 81     private int defHealth, h, hitForce;
 82     private bool hitColor = false;
 83     /// <summary>
 84     /// 下一个闪烁,停止闪烁时间
 85     /// </summary>
 86     private float nextFlash, stopFlashTime;
 87     /// <summary>
 88     /// 扔物体
 89     /// </summary>
 90     private Throwing throwing;
 91     /// <summary>
 92     /// 闪烁渲染器
 93     /// </summary>
 94     private Renderer flashRender;
 95     /// <summary>
 96     /// AudioSource
 97     /// </summary>
 98     private AudioSource aSource;
 99     
100     void Awake()
101     {
102         aSource = GetComponent<AudioSource>();
103         if(currentHealth <= 0)
104             Debug.LogWarning(transform.name + " has 'currentHealth' set to 0 or less in 'Health' script: it has died upon scene start");
105         aSource.playOnAwake = false;
106         if(flashObject == null)
107             flashObject = transform;
108         flashRender = flashObject.GetComponent<Renderer>();
109         originalColor = flashRender.material.color;
110         defHealth = currentHealth;
111         respawnPos = transform.position;
112     }
113     
114 
115     void Update()
116     {        
117         //受到伤害,闪烁
118         if (currentHealth < h)
119         {
120             flashing = true;
121             stopFlashTime = Time.time + flashDuration;
122             if (hurtSound)
123                 AudioSource.PlayClipAtPoint(hurtSound, transform.position);
124         }
125         h = currentHealth;
126         
127         if (flashing)
128         {
129             Flash ();
130             if (Time.time > stopFlashTime)
131             {
132                 flashRender.material.color = originalColor;
133                 flashing = false;
134             }
135         }
136         
137         dead = (currentHealth <= 0) ? true : false;
138         if (dead)
139             Death();
140     }
141     
142     /// <summary>
143     /// 闪烁
144     /// </summary>
145     void Flash()
146     {
147         flashRender.material.color = (hitColor) ? hitFlashColor : originalColor;
148         if(Time.time > nextFlash)
149         {
150             hitColor = !hitColor;
151             nextFlash = Time.time + hitFlashDelay;
152         }
153     }
154     
155     /// <summary>
156     /// 死亡
157     /// </summary>
158     void Death()
159     {
160         
161         if(tag == "Player")
162             throwing = GetComponent<Throwing>();
163         if(throwing && throwing.heldObj && throwing.heldObj.tag == "Pickup")
164             throwing.ThrowPickup();
165         
166         if (deadSound)
167             AudioSource.PlayClipAtPoint(deadSound, transform.position);
168         flashing = false;
169         flashObject.GetComponent<Renderer>().material.color = originalColor;
170         if(respawn)
171         {
172             Rigidbody rigid = GetComponent<Rigidbody>();
173             if(rigid)
174                 rigid.velocity *= 0;
175             transform.position = respawnPos;
176             dead = false;
177             currentHealth = defHealth;
178         }
179         else
180             Destroy (gameObject);
181         
182         if (spawnOnDeath.Length != 0)
183             foreach(GameObject obj in spawnOnDeath)
184                 Instantiate(obj, transform.position, Quaternion.Euler(Vector3.zero));
185     }
186     
187     void OnCollisionEnter(Collision col)
188     {
189         //播放撞击声音
190         if(!aSource.isPlaying && impactSound)
191         {
192             aSource.clip = impactSound;
193             aSource.volume = col.relativeVelocity.magnitude/30;
194             aSource.Play();
195         }
196             
197         //不会受到撞击伤害,返回
198         if (!takeImpactDmg)
199             return;
200         //找到标签,返回
201         foreach(string tag in impactFilterTag)            
202             if(col.transform.tag == tag)
203                 return;
204         //只能受到刚体撞击且没有刚体,返回
205         if(onlyRigidbodyImpact && !col.rigidbody)
206             return;
207         
208         //计算受到的伤害
209         if(col.rigidbody)
210             hitForce = (int)(col.rigidbody.velocity.magnitude/4 * col.rigidbody.mass);
211         else
212             hitForce = (int)col.relativeVelocity.magnitude/6;
213         currentHealth -= hitForce;
214         //print (transform.name + " took: " + hitForce + " dmg in collision with " + col.transform.name);
215     }
216 }
Health
  1 using UnityEngine;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 
  5 /// <summary>
  6 /// 移动到指定的点
  7 /// </summary>
  8 [RequireComponent(typeof(Rigidbody))]
  9 public class MoveToPoints:MonoBehaviour {
 10     /// <summary>
 11     /// 移动速度
 12     /// </summary>
 13     public float speed;                                        
 14     /// <summary>
 15     /// 延迟
 16     /// </summary>
 17     public float delay;                                        
 18     /// <summary>
 19     /// 移动类型
 20     /// </summary>
 21     public type movementType;                               
 22 
 23     /// <summary>
 24     /// 移动类型
 25     /// </summary>
 26     public enum type {
 27         /// <summary>
 28         /// 在最后一个路标停止
 29         /// </summary>
 30         PlayOnce,
 31         /// <summary>
 32         /// 转圈
 33         /// </summary>
 34         Loop,
 35         /// <summary>
 36         /// 来回
 37         /// </summary>
 38         PingPong
 39     }
 40     /// <summary>
 41     /// 当前路标索引
 42     /// </summary>
 43     private int currentWp;
 44     /// <summary>
 45     /// 到达时间
 46     /// </summary>
 47     private float arrivalTime;
 48     /// <summary>
 49     /// 是否向前,是否到达
 50     /// </summary>
 51     private bool forward = true, arrived = false;
 52     /// <summary>
 53     /// 路标列表
 54     /// </summary>
 55     private List<Transform> waypoints = new List<Transform>();
 56     /// <summary>
 57     /// 角色移动
 58     /// </summary>
 59     private CharacterMotor characterMotor;
 60     /// <summary>
 61     /// 敌人AI
 62     /// </summary>
 63     private EnemyAI enemyAI;
 64     /// <summary>
 65     /// 刚体
 66     /// </summary>
 67     private Rigidbody rigid;
 68 
 69     void Awake() {
 70         //标签不是Enemy
 71         if(transform.tag != "Enemy") {
 72             //没有刚体,添加刚体
 73             if(!GetComponent<Rigidbody>())
 74                 gameObject.AddComponent<Rigidbody>();
 75             //动力学
 76             GetComponent<Rigidbody>().isKinematic = true;
 77             //不使用重力
 78             GetComponent<Rigidbody>().useGravity = false;
 79             //修改插值类型
 80             GetComponent<Rigidbody>().interpolation = RigidbodyInterpolation.Interpolate;
 81         } else {
 82             //角色移动
 83             characterMotor = GetComponent<CharacterMotor>();
 84             //获取敌人AI
 85             enemyAI = GetComponent<EnemyAI>();
 86         }
 87         //获取刚体
 88         rigid = GetComponent<Rigidbody>();
 89         //遍历子物体
 90         foreach(Transform child in transform)
 91             //添加路标
 92             if(child.tag == "Waypoint")
 93                 waypoints.Add(child);
 94         //分离路标
 95         foreach(Transform waypoint in waypoints)
 96             waypoint.parent = null;
 97         
 98         if(waypoints.Count == 0)
 99             Debug.LogError("No waypoints found for 'MoveToPoints' script. To add waypoints: add child gameObjects with the tag 'Waypoint'",transform);
100     }
101 
102 
103     void Update() {
104         //路标数量大于0
105         if(waypoints.Count > 0) {
106             //没有到达
107             if(!arrived) {
108                 //与下一个路标的距离小于0.3
109                 if(Vector3.Distance(transform.position,waypoints[currentWp].position) < 0.3f) {
110                     //设置到达时间
111                     arrivalTime = Time.time;
112                     //修改到达标志位
113                     arrived = true;
114                 }
115             //到达
116             } else {
117                 //当前时间大于到达时间加延迟
118                 if(Time.time > arrivalTime + delay) {
119                     //获取下一个路标
120                     GetNextWP();
121                     //修改到达标志位
122                     arrived = false;
123                 }
124             }
125         }
126         //标签是Enemy,路标数量大于0
127         if(transform.tag == "Enemy" && waypoints.Count > 0) {
128             //没有到达位置
129             if(!arrived) {
130                 //玩家移动到指定路标
131                 characterMotor.MoveTo(waypoints[currentWp].position,enemyAI.acceleration,0.1f,enemyAI.ignoreY);
132                 //播放动画
133                 if(enemyAI.animatorController)
134                     enemyAI.animatorController.SetBool("Moving",true);
135             //到达位置
136             } else
137                 //播放动画
138                 if(enemyAI.animatorController)
139                     enemyAI.animatorController.SetBool("Moving",false);
140         }
141     }
142 
143     void FixedUpdate() {
144         //标签为Enemy
145         if(transform.tag != "Enemy") {
146             //没有到达且路标数量大于0
147             if(!arrived && waypoints.Count > 0) {
148                 //计算和路标的距离
149                 Vector3 direction = waypoints[currentWp].position - transform.position;
150                 //刚体移动
151                 rigid.MovePosition(transform.position + (direction.normalized * speed * Time.fixedDeltaTime));
152             }
153         }
154     }
155 
156     /// <summary>
157     /// 获取下一个路标
158     /// </summary>
159     private void GetNextWP() {
160         //一次
161         if(movementType == type.PlayOnce) {
162             currentWp++;
163             if(currentWp == waypoints.Count)
164                 enabled = false;
165         }
166         //循环
167         if(movementType == type.Loop)
168             currentWp = (currentWp == waypoints.Count - 1) ? 0 : currentWp += 1;
169         //来回
170         if(movementType == type.PingPong) {
171             if(currentWp == waypoints.Count - 1)
172                 forward = false;
173             else if(currentWp == 0)
174                 forward = true;
175             currentWp = (forward) ? currentWp += 1 : currentWp -= 1;
176         }
177     }
178 
179 
180     void OnDrawGizmos() {
181         Gizmos.color = Color.cyan;
182         foreach(Transform child in transform) {
183             if(child.tag == "Waypoint")
184                 Gizmos.DrawSphere(child.position,.7f);
185         }
186     }
187 }
MoveToPoints
  1 using UnityEngine;
  2 using System.Collections;
  3 
  4 /// <summary>
  5 /// 玩家移动
  6 /// </summary>
  7 [RequireComponent(typeof(CharacterMotor))]
  8 [RequireComponent(typeof(DealDamage))]
  9 [RequireComponent(typeof(AudioSource))]
 10 [RequireComponent(typeof(Rigidbody))]
 11 public class PlayerMove:MonoBehaviour {
 12     /// <summary>
 13     /// 是否是卷轴模式
 14     /// </summary>
 15     public bool sidescroller;
 16     /// <summary>
 17     /// 主相机,FloorChecks游戏物体Transform
 18     /// </summary>
 19     public Transform mainCam, floorChecks;
 20     /// <summary>
 21     /// Animator
 22     /// </summary>
 23     public Animator animator;
 24     /// <summary>
 25     /// 跳跃声音
 26     /// </summary>
 27     public AudioClip jumpSound;
 28     /// <summary>
 29     /// 着陆声音
 30     /// </summary>
 31     public AudioClip landSound;
 32 
 33     /// <summary>
 34     /// 加速度
 35     /// </summary>
 36     public float accel = 70f;
 37     /// <summary>                                                                                     
 38     /// 空中加速度
 39     /// </summary>
 40     public float airAccel = 18f;
 41     /// <summary>
 42     /// 减速度
 43     /// </summary>    
 44     public float decel = 7.6f;
 45     /// <summary>
 46     /// 空中减速度
 47     /// </summary>
 48     public float airDecel = 1.1f;
 49     /// <summary>
 50     /// 旋转速度,空中旋转速度
 51     /// </summary>
 52     [Range(0f,5f)]
 53     public float rotateSpeed = 0.7f, airRotateSpeed = 0.4f;
 54     /// <summary>
 55     /// 最高移动速度
 56     /// </summary>
 57     public float maxSpeed = 9;
 58     /// <summary>
 59     /// 最大坡度,最大滑坡速度
 60     /// </summary>
 61     public float slopeLimit = 40, slideAmount = 35;
 62     /// <summary>
 63     /// 移动平台的摩擦力
 64     /// </summary>
 65     public float movingPlatformFriction = 7.7f;
 66 
 67     /// <summary>
 68     /// 常规跳跃力
 69     /// </summary>
 70     public Vector3 jumpForce = new Vector3(0,13,0);
 71     /// <summary>
 72     /// 2连跳力
 73     /// </summary>
 74     public Vector3 secondJumpForce = new Vector3(0,13,0);
 75     /// <summary>
 76     /// 3连跳力
 77     /// </summary>
 78     public Vector3 thirdJumpForce = new Vector3(0,13,0);
 79     /// <summary>
 80     /// 两次跳跃间的延迟
 81     /// </summary>
 82     public float jumpDelay = 0.1f;
 83     /// <summary>
 84     /// 着陆前仍然可以按下跳跃的时间
 85     /// </summary>
 86     public float jumpLeniancy = 0.17f;
 87     [HideInInspector]
 88     public int onEnemyBounce;
 89     /// <summary>
 90     /// 跳跃类型
 91     /// </summary>
 92     private int onJump;
 93     /// <summary>
 94     /// 是否着陆
 95     /// </summary>
 96     private bool grounded;
 97     /// <summary>
 98     /// 检查是否着陆的Transform列表(玩家身体的下面9个点)
 99     /// </summary>
100     private Transform[] floorCheckers;
101     /// <summary>
102     /// 
103     /// </summary>
104     private Quaternion screenMovementSpace;
105     /// <summary>
106     /// 按下跳跃的时间,着陆后的时间,当前加速度,当前减速度,当前旋转速度,斜率
107     /// </summary>
108     private float airPressTime, groundedCount, curAccel, curDecel, curRotateSpeed, slope;
109     /// <summary>
110     /// 方向,移动方向,屏幕向前移动,屏幕向右移动,移动物体的速度
111     /// </summary>
112     private Vector3 direction, moveDirection, screenMovementForward, screenMovementRight, movingObjSpeed;
113 
114     /// <summary>
115     /// 角色移动
116     /// </summary>
117     private CharacterMotor characterMotor;
118     /// <summary>
119     /// 敌人AI
120     /// </summary>
121     private EnemyAI enemyAI;
122     /// <summary>
123     /// 处理伤害
124     /// </summary>
125     private DealDamage dealDamage;
126     /// <summary>
127     /// Rigidbody
128     /// </summary>
129     private Rigidbody rigid;
130     /// <summary>
131     /// AudioSource
132     /// </summary>
133     private AudioSource aSource;
134 
135 
136     void Awake() {
137         //没有检查列表
138         if(!floorChecks) {
139             //添加
140             floorChecks = new GameObject().transform;
141             floorChecks.name = "FloorChecks";
142             floorChecks.parent = transform;
143             floorChecks.position = transform.position;
144             //添加单个子物体
145             GameObject check = new GameObject();
146             check.name = "Check1";
147             check.transform.parent = floorChecks;
148             check.transform.position = transform.position;
149             Debug.LogWarning("No 'floorChecks' assigned to PlayerMove script, so a single floorcheck has been created",floorChecks);
150         }
151         //标签不是Player
152         if(tag != "Player") {
153             //标签改为Player
154             tag = "Player";
155             Debug.LogWarning("PlayerMove script assigned to object without the tag 'Player', tag has been assigned automatically",transform);
156         }
157 
158         //获取主摄像机
159         mainCam = GameObject.FindGameObjectWithTag("MainCamera").transform;
160         //获取处理伤害
161         dealDamage = GetComponent<DealDamage>();
162         //获取角色移动
163         characterMotor = GetComponent<CharacterMotor>();
164         //获取刚体
165         rigid = GetComponent<Rigidbody>();
166         //获取AudioSource
167         aSource = GetComponent<AudioSource>();
168 
169         //设置检查列表
170         floorCheckers = new Transform[floorChecks.childCount];
171         for(int i = 0;i < floorCheckers.Length;i++)
172             floorCheckers[i] = floorChecks.GetChild(i);
173     }
174 
175     void Update() {
176         //激活刚体
177         rigid.WakeUp();
178         //计算跳跃
179         JumpCalculations();
180 
181         //根据玩家是否着地设置当前加速度
182         curAccel = (grounded) ? accel : airAccel;
183         //根据玩家是否着地设置当前减速度
184         curDecel = (grounded) ? decel : airDecel;
185         //根据玩家是否着地设置当前旋转速度
186         curRotateSpeed = (grounded) ? rotateSpeed : airRotateSpeed;
187 
188         screenMovementSpace = Quaternion.Euler(0,mainCam.eulerAngles.y,0);
189         screenMovementForward = screenMovementSpace * Vector3.forward;
190         screenMovementRight = screenMovementSpace * Vector3.right;
191 
192 
193         float h = Input.GetAxisRaw("Horizontal");
194         float v = Input.GetAxisRaw("Vertical");
195 
196         //不是卷轴模式
197         if(!sidescroller)
198             direction = (screenMovementForward * v) + (screenMovementRight * h);
199         //是卷轴模式
200         else
201             //方向等于水平轴的输入*Vector3.right
202             direction = Vector3.right * h;
203         //移动方向等于当前位置加方向
204         moveDirection = transform.position + direction;
205     }
206 
207     void FixedUpdate() {
208         //检查是否着地
209         grounded = IsGrounded();
210         //角色移动
211         characterMotor.MoveTo(moveDirection,curAccel,0.7f,true);
212         //旋转角色
213         if(rotateSpeed != 0 && direction.magnitude != 0)
214             characterMotor.RotateToDirection(moveDirection,curRotateSpeed * 5,true);
215         //管理角色速度
216         characterMotor.ManageSpeed(curDecel,maxSpeed + movingObjSpeed.magnitude,true);
217         //播放动画
218         if(animator) {
219             animator.SetFloat("DistanceToTarget",characterMotor.DistanceToTarget);
220             animator.SetBool("Grounded",grounded);
221             animator.SetFloat("YVelocity",GetComponent<Rigidbody>().velocity.y);
222         }
223     }
224 
225     void OnCollisionStay(Collision other) {
226         if(other.collider.tag != "Untagged" || grounded == false)
227             return;
228         //在小坡度上停止下滑
229         if(direction.magnitude == 0 && slope < slopeLimit && rigid.velocity.magnitude < 2) {
230             rigid.velocity = Vector3.zero;
231         }
232     }
233 
234     /// <summary>
235     /// 检查是否着陆
236     /// </summary>
237     private bool IsGrounded() {
238         //计算距离
239         float dist = GetComponent<Collider>().bounds.extents.y;
240         
241         //遍历所有检查点
242         foreach(Transform check in floorCheckers) {
243             RaycastHit hit;
244             //向下发射射线
245             if(Physics.Raycast(check.position,Vector3.down,out hit,dist + 0.05f)) {
246                 //如果不是触发器
247                 if(!hit.transform.GetComponent<Collider>().isTrigger) {
248                     //计算斜率
249                     slope = Vector3.Angle(hit.normal,Vector3.up);
250                     //斜率大于最大斜率,不是Pushable
251                     if(slope > slopeLimit && hit.transform.tag != "Pushable") {
252                         //计算滑动方向
253                         Vector3 slide = new Vector3(0f,-slideAmount,0f);
254                         //刚体施加滑动力
255                         rigid.AddForce(slide,ForceMode.Force);
256                     }
257                     //碰到敌人,y轴速度小于0
258                     if(hit.transform.tag == "Enemy" && rigid.velocity.y < 0) {
259                         //获取敌人AI
260                         enemyAI = hit.transform.GetComponent<EnemyAI>();
261                         //弹开玩家
262                         enemyAI.BouncedOn();
263                         //
264                         onEnemyBounce++;
265                         //处理伤害
266                         dealDamage.Attack(hit.transform.gameObject,1,0f,0f);
267                     } else
268                         //
269                         onEnemyBounce = 0;
270                     //移动平台或Pushable
271                     if(hit.transform.tag == "MovingPlatform" || hit.transform.tag == "Pushable") {
272                         //移动物体的速度为刚体的速度
273                         movingObjSpeed = hit.transform.GetComponent<Rigidbody>().velocity;
274                         //移动物体的y轴速度为0
275                         movingObjSpeed.y = 0f;
276                         //根据移动物体的速度和摩檫力给玩家刚体添加力
277                         rigid.AddForce(movingObjSpeed * movingPlatformFriction * Time.fixedDeltaTime,ForceMode.VelocityChange);
278                     } else {
279                         //移动物体的速度归0
280                         movingObjSpeed = Vector3.zero;
281                     }
282                     return true;
283                 }
284             }
285         }
286         //移动物体的速度归0
287         movingObjSpeed = Vector3.zero;
288         return false;
289     }
290 
291     /// <summary>
292     /// 计算跳跃
293     /// </summary>
294     private void JumpCalculations() {
295         //保存着陆后的时间
296         groundedCount = (grounded) ? groundedCount += Time.deltaTime : 0f;
297 
298         //着陆时间小于0.25并且不等于0,正在播放声音,y轴的速度小于1
299         if(groundedCount < 0.25 && groundedCount != 0 && !GetComponent<AudioSource>().isPlaying && landSound && GetComponent<Rigidbody>().velocity.y < 1) {
300             //根据y轴的速度修改音量
301             aSource.volume = Mathf.Abs(GetComponent<Rigidbody>().velocity.y) / 40;
302             //修改声音
303             aSource.clip = landSound;
304             //播放声音
305             aSource.Play();
306         }
307         //按下jump且没有着陆
308         if(Input.GetButtonDown("Jump") && !grounded)
309             //修改按下跳跃的时间为当前时间
310             airPressTime = Time.time;
311 
312         //着陆,斜率小于最大斜率
313         if(grounded && slope < slopeLimit) {
314             //按下Jump
315             if(Input.GetButtonDown("Jump") || airPressTime + jumpLeniancy > Time.time) {
316                 //切换跳跃类型
317                 onJump = (groundedCount < jumpDelay) ? Mathf.Min(2,onJump + 1) : 0;
318 
319                 if(onJump == 0)
320                     Jump(jumpForce);
321                 else if(onJump == 1)
322                     Jump(secondJumpForce);
323                 else if(onJump == 2) {
324                     Jump(thirdJumpForce);
325                     onJump--;
326                 }
327             }
328         }
329     }
330 
331     /// <summary>
332     /// 跳跃
333     /// </summary>
334     public void Jump(Vector3 jumpVelocity) {
335         //播放跳跃声音
336         if(jumpSound) {
337             aSource.volume = 1;
338             aSource.clip = jumpSound;
339             aSource.Play();
340         }
341         //改变刚体的y轴速度
342         rigid.velocity = new Vector3(rigid.velocity.x,0f,rigid.velocity.z);
343         //给刚体添加力
344         rigid.AddRelativeForce(jumpVelocity,ForceMode.Impulse);
345         //重置按下跳跃的时间
346         airPressTime = 0f;
347     }
348 }
PlayerMove
  1 using UnityEngine;
  2 using System.Collections;
  3 
  4 
  5 /// <summary>
  6 /// 让玩家拾取,扔,推物体
  7 /// </summary>
  8 [RequireComponent(typeof(AudioSource))]
  9 [RequireComponent(typeof(Rigidbody))]
 10 [RequireComponent(typeof(PlayerMove))]
 11 public class Throwing : MonoBehaviour 
 12 {
 13     /// <summary>
 14     /// 拾取声音
 15     /// </summary>
 16     public AudioClip pickUpSound;                                
 17     /// <summary>
 18     /// 扔声音
 19     /// </summary>
 20     public AudioClip throwSound;                                         
 21     /// <summary>
 22     /// 用来抓物体的子游戏物体
 23     /// </summary>
 24     public GameObject grabBox;                                    
 25     /// <summary>
 26     /// 举起物体的偏移
 27     /// </summary>
 28     public Vector3 holdOffset;                                    
 29     /// <summary>
 30     /// 扔的力
 31     /// </summary>
 32     public Vector3 throwForce = new Vector3(0, 5, 7);            
 33     /// <summary>
 34     /// 转向物体的速度
 35     /// </summary>
 36     public float rotateToBlockSpeed = 3;                        
 37     /// <summary>
 38     /// 检查玩家头部的范围
 39     /// </summary>
 40     public float checkRadius = 0.5f;                            
 41     /// <summary>
 42     /// 拾取物体的重量
 43     /// </summary>
 44     [Range(0.1f, 1f)]                                            
 45     public float weightChange = 0.3f;
 46     /// <summary>
 47     /// 未用
 48     /// </summary>                    
 49     [Range(10f, 1000f)]
 50     public float holdingBreakForce = 45, holdingBreakTorque = 45;
 51     /// <summary>
 52     /// Animator
 53     /// </summary>
 54     public Animator animator;                                    
 55     /// <summary>
 56     /// 手臂动画层
 57     /// </summary>
 58     public int armsAnimationLayer;                                
 59     
 60     /// <summary>
 61     /// 举起或抓住的物体
 62     /// </summary>
 63     [HideInInspector]
 64     public GameObject heldObj;
 65     /// <summary>
 66     /// 举起物体的位置
 67     /// </summary>
 68     private Vector3 holdPos;
 69     /// <summary>
 70     /// 连接物体和角色的关节
 71     /// </summary>
 72     private FixedJoint joint;
 73     /// <summary>
 74     /// 举起物体的时间,扔物体的时间,默认旋转速度
 75     /// </summary>
 76     private float timeOfPickup, timeOfThrow, defRotateSpeed;
 77     /// <summary>
 78     /// gizmo颜色
 79     /// </summary>
 80     private Color gizmoColor;
 81     /// <summary>
 82     /// AudioSource
 83     /// </summary>
 84     private AudioSource aSource;
 85     /// <summary>
 86     /// 玩家移动
 87     /// </summary>
 88     private PlayerMove playerMove;
 89     /// <summary>
 90     /// 父物体下的子物体的触发器
 91     /// </summary>
 92     private TriggerParent triggerParent;
 93     /// <summary>
 94     /// 默认刚体插值类型
 95     /// </summary>
 96     private RigidbodyInterpolation objectDefInterpolation;
 97     
 98     
 99 
100     void Awake()
101     {
102         //获取AudioSource
103         aSource = GetComponent<AudioSource>();
104         //没有抓物体的子物体
105         if(!grabBox)
106         {
107             //新建
108             grabBox = new GameObject();
109             //添加碰撞器
110             grabBox.AddComponent<BoxCollider>();
111             //设置为触发器
112             grabBox.GetComponent<Collider>().isTrigger = true;
113             //设置玩家为父物体
114             grabBox.transform.parent = transform;
115             //修改局部坐标
116             grabBox.transform.localPosition = new Vector3(0f, 0f, 0.5f);
117             //修改层(Ignore Raycast)
118             grabBox.layer = 2;
119             Debug.LogWarning("No grabBox object assigned to 'Throwing' script, one has been created and assigned for you", grabBox);
120         }
121         //获取玩家移动脚本
122         playerMove = GetComponent<PlayerMove>();
123         //设置默认旋转速度
124         defRotateSpeed = playerMove.rotateSpeed;
125         //Animator不为空
126         if(animator)
127             //设置手臂动画层的权重
128             animator.SetLayerWeight(armsAnimationLayer, 1);
129     }
130     
131     void Update()
132     {
133         //按下Grab键,有举起或抓住的物体,当前时间比举起物体的时间大0.1f
134         if (Input.GetButtonDown ("Grab") && heldObj && Time.time > timeOfPickup + 0.1f)
135         {
136             //举起物体的标签为"Pickup"
137             if(heldObj.tag == "Pickup")
138                 //扔掉举起的物体 
139                 ThrowPickup();
140         }
141         //有Animator
142         if(animator)
143             //有举起的物体且物体标签为"Pickup"
144             if(heldObj && heldObj.tag == "Pickup")
145                 animator.SetBool ("HoldingPickup", true);
146             else
147                 animator.SetBool ("HoldingPickup", false);
148             //有抓住的物体且物体标签为"Pushable"
149             if(heldObj && heldObj.tag == "Pushable")
150                 animator.SetBool ("HoldingPushable", true);
151             else
152                 animator.SetBool ("HoldingPushable", false);
153         
154         //有抓住的物体且物体标签为"Pushable"
155         if (heldObj && heldObj.tag == "Pushable")
156         {
157             //按下Grab
158             if(Input.GetButtonUp ("Grab"))
159             {
160                 //丢弃物体
161                 DropPushable();
162             }
163             //没有关节
164             if(!joint)
165             {
166                 //丢弃物体
167                 DropPushable();
168                 print ("'Pushable' object dropped because the 'holdingBreakForce' or 'holdingBreakTorque' was exceeded");
169             }
170         }
171     }
172     
173     void OnTriggerStay(Collider other)
174     {
175         //按下Grab
176         if(Input.GetButton("Grab"))
177         {
178             //标签为"Pickup",没有举起的物体,当前时间大于扔物体的时间加0.2
179             if(other.tag == "Pickup" && heldObj == null && timeOfThrow + 0.2f < Time.time)
180                 //举起物体
181                 LiftPickup(other);
182             //标签为"Pushable",没有抓住的物体,当前时间大于扔物体的时间加0.2
183             if(other.tag == "Pushable" && heldObj == null && timeOfThrow + 0.2f < Time.time)
184                 //抓物体
185                 GrabPushable(other);
186         }
187     }
188             
189     /// <summary>
190     /// 抓住物体
191     /// </summary>
192     private void GrabPushable(Collider other)
193     {
194         //抓住的物体为碰撞的物体
195         heldObj = other.gameObject;
196         //获取物体的插值类型
197         objectDefInterpolation = heldObj.GetComponent<Rigidbody>().interpolation;
198         //修改物体的插值为内插值
199         heldObj.GetComponent<Rigidbody>().interpolation = RigidbodyInterpolation.Interpolate;
200         //添加关节,连接玩家和物体
201         AddJoint ();
202         //设置破坏关节的力为无限
203         joint.breakForce = Mathf.Infinity;
204         //设置破坏关节的扭矩力为无限
205         joint.breakTorque = Mathf.Infinity;
206         //设置玩家移动的旋转速度为0
207         playerMove.rotateSpeed = 0;
208     }
209     
210     /// <summary>
211     /// 举起物体
212     /// </summary>
213     private void LiftPickup(Collider other)
214     {
215         //获取物体的网格
216         Mesh otherMesh = other.GetComponent<MeshFilter>().mesh;
217         //计算举起的位置
218         holdPos = transform.position + transform.forward * holdOffset.z + transform.right * holdOffset.x + transform.up * holdOffset.y;
219         //举起的位置的y值加碰撞器的范围的y值加碰撞物体网格的范围的y值
220         holdPos.y += (GetComponent<Collider>().bounds.extents.y) + (otherMesh.bounds.extents.y);
221 
222         //检测举起位置的圆形范围
223         if(!Physics.CheckSphere(holdPos,checkRadius)) {
224             //修改gizmo颜色
225             gizmoColor = Color.green;
226             //缓存举起的物体
227             heldObj = other.gameObject;
228             //修改默认插值类型
229             objectDefInterpolation = heldObj.GetComponent<Rigidbody>().interpolation;
230             //修改举起物体的插值类型
231             heldObj.GetComponent<Rigidbody>().interpolation = RigidbodyInterpolation.Interpolate;
232             //修改举起物体的位置
233             heldObj.transform.position = holdPos;
234             //修改举起物体的旋转
235             heldObj.transform.rotation = transform.rotation;
236             //添加关节,连接物体和玩家
237             AddJoint();
238             //修改举起物体的质量
239             heldObj.GetComponent<Rigidbody>().mass *= weightChange;
240             //修改举起物体的时间为当前时间
241             timeOfPickup = Time.time;
242         } else {
243             //修改gizmo颜色
244             gizmoColor = Color.red;
245             print("Can't lift object here. If nothing is above the player, perhaps you need to add a layerMask parameter to line 136 of the code in this script," +
246                 "the CheckSphere function, in order to make sure it isn't detecting something above the players head that is invisible");
247         }
248     }
249     
250     /// <summary>
251     /// 放下物体
252     /// </summary>
253     private void DropPushable()
254     {
255         //修改抓住物体的插值类型
256         heldObj.GetComponent<Rigidbody>().interpolation = objectDefInterpolation;
257         //销毁关节
258         Destroy (joint);
259         //修改玩家移动的旋转速度为默认旋转速度
260         playerMove.rotateSpeed = defRotateSpeed;
261         heldObj = null;
262         timeOfThrow = Time.time;
263     }
264     
265     /// <summary>
266     /// 扔掉物体
267     /// </summary>
268     public void ThrowPickup()
269     {
270         //播放扔物体声音
271         if(throwSound)
272         {
273             aSource.volume = 1;
274             aSource.clip = throwSound;
275             aSource.Play ();
276         }
277         //删除关节
278         Destroy (joint);
279         //获取举起物体的刚体
280         Rigidbody r = heldObj.GetComponent<Rigidbody>();
281         //恢复物体的插值类型
282         r.interpolation = objectDefInterpolation;
283         //恢复物体的质量
284         r.mass /= weightChange;
285         //给刚体添加力
286         r.AddRelativeForce (throwForce, ForceMode.VelocityChange);
287 
288         heldObj = null;
289         timeOfThrow = Time.time;
290     }
291     
292     /// <summary>
293     /// 添加关节,连接玩家和物体
294     /// </summary>
295     private void AddJoint()
296     {
297         //如果有物体
298         if (heldObj)
299         {
300             //播放拾取声音
301             if(pickUpSound)
302             {
303                 aSource.volume = 1;
304                 aSource.clip = pickUpSound;
305                 aSource.Play ();
306             }
307             //在物体上添加关节
308             joint = heldObj.AddComponent<FixedJoint>();
309             //设置关节的连接物体为玩家的刚体
310             joint.connectedBody = GetComponent<Rigidbody>();
311         }
312     }
313     
314     void OnDrawGizmosSelected()
315     {
316         Gizmos.color = gizmoColor;
317         Gizmos.DrawSphere (holdPos, checkRadius);
318     }
319 }
Throwing
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 /// <summary>
 5 /// 父物体下的子物体的触发器
 6 /// </summary>
 7 public class TriggerParent:MonoBehaviour {
 8 
 9     /// <summary>
10     /// 要检查的标签列表
11     /// </summary>
12     public string[] tagsToCheck;            
13 
14     /// <summary>
15     /// 是否发生了碰撞,是否在碰撞中
16     /// </summary>
17     [HideInInspector]
18     public bool collided, colliding;
19     /// <summary>
20     /// 撞击的物体
21     /// </summary>
22     [HideInInspector]
23     public GameObject hitObject;
24 
25     void Awake() {
26         //没有Collider或有Collider但不是触发器
27         if(!GetComponent<Collider>() || (GetComponent<Collider>() && !GetComponent<Collider>().isTrigger))
28             Debug.LogError("'TriggerParent' script attached to object which does not have a trigger collider",transform);
29     }
30 
31     void OnTriggerEnter(Collider other) {
32         //标签列表长度大于0且没有碰撞过
33         if(tagsToCheck.Length > 0 && !collided) {
34             //遍历标签
35             foreach(string tag in tagsToCheck) {
36                 //找到标签
37                 if(other.tag == tag) {
38                     //已发生碰撞
39                     collided = true;
40                     //存储碰撞物体
41                     hitObject = other.gameObject;
42                     break;
43                 }
44 
45             }
46         } else
47             //已发生碰撞
48             collided = true;
49         //存储碰撞物体
50         hitObject = other.gameObject;
51     }
52 
53     void OnTriggerStay(Collider other) {
54         //标签列表长度大于0
55         if(tagsToCheck.Length > 0) {
56             //遍历标签
57             foreach(string tag in tagsToCheck) {
58                 //找到标签
59                 if(other.tag == tag) {
60                     //正在碰撞
61                     colliding = true;
62                     //存储碰撞物体
63                     hitObject = other.gameObject;
64                     break;
65                 }
66             }
67         } else {
68             //存储碰撞物体
69             hitObject = other.gameObject;
70             //正在碰撞
71             colliding = true;
72         }
73     }
74 
75     void OnTriggerExit(Collider other) {
76         //标签列表长度大于0
77         if(tagsToCheck.Length > 0) {
78             //遍历标签
79             foreach(string tag in tagsToCheck) {
80                 //找到标签
81                 if(other.tag == tag) {
82                     colliding = false;
83                     hitObject = null;
84                     break;
85                 }
86             }
87         } else
88             return;
89     }
90 
91     void LateUpdate() {
92         //重置collided,hitObject
93         if(collided) {
94             collided = false;
95             hitObject = null;
96         }
97     }
98 }
TriggerParent
  1 using UnityEngine;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 
  5 /// <summary>
  6 ///  7 /// </summary>
  8 [RequireComponent(typeof(BoxCollider))]
  9 public class Water:MonoBehaviour {
 10     /// <summary>
 11     /// 玩家进水的声音
 12     /// </summary>
 13     public AudioClip splashSound;
 14     /// <summary>
 15     /// 水的推力
 16     /// </summary>
 17     public Vector3 force = new Vector3(0,16.5f,0);
 18     /// <summary>
 19     /// 玩家是否会受到水的阻力影响
 20     /// </summary>
 21     public bool effectPlayerDrag;
 22     /// <summary>
 23     /// 刚体受到的水的阻力(不包含玩家)
 24     /// </summary>
 25     public float resistance = 0.4f;                     
 26     /// <summary>
 27     /// 刚体受到的角阻力(不包含玩家)
 28     /// </summary>
 29     public float angularResistance = 0.2f;                
 30 
 31     /// <summary>
 32     /// 刚体受到的阻力字典
 33     /// </summary>
 34     private Dictionary<GameObject,float> dragStore = new Dictionary<GameObject,float>();
 35     /// <summary>
 36     /// 刚体受到的角阻力字典
 37     /// </summary>
 38     private Dictionary<GameObject,float> angularStore = new Dictionary<GameObject,float>();
 39 
 40     void Awake() {
 41         //自动修改"Water"标签
 42         if(tag != "Water") {
 43             tag = "Water";
 44             Debug.LogWarning("'Water' script attached to an object not tagged 'Water', it been assigned the tag 'Water'",transform);
 45         }
 46         //设置Trigger
 47         GetComponent<Collider>().isTrigger = true;
 48     }
 49 
 50     void OnTriggerEnter(Collider other) {
 51         //获取物体的刚体
 52         Rigidbody r = other.GetComponent<Rigidbody>();
 53         //刚体不为空
 54         if(r) {
 55             //播放入水声音
 56             if(splashSound) {
 57                 float volume = other.GetComponent<Rigidbody>().velocity.magnitude / 5;
 58                 AudioSource.PlayClipAtPoint(splashSound,other.transform.position,volume);
 59             }
 60             
 61             //是玩家且不受水的阻力影响
 62             if(r.tag == "Player" && !effectPlayerDrag)
 63                 return;
 64 
 65             //存储受到的阻力
 66             dragStore.Add(r.gameObject,r.drag);
 67             //存储受到的角阻力
 68             angularStore.Add(r.gameObject,r.angularDrag);
 69 
 70             //修改刚体受到的阻力
 71             r.drag = resistance;
 72             //修改刚体受到的角阻力
 73             r.angularDrag = angularResistance;
 74         } else if(splashSound)
 75             //播放入水声音
 76             AudioSource.PlayClipAtPoint(splashSound,other.transform.position);
 77     }
 78 
 79 
 80 
 81     void OnTriggerStay(Collider other) {
 82         //计算表面高度
 83         float surface = transform.position.y + GetComponent<Collider>().bounds.extents.y;
 84         Rigidbody rigid = other.GetComponent<Rigidbody>();
 85         if(rigid) {
 86             //计算物体相对于表面高度的深度
 87             float depth = surface - other.transform.position.y;
 88             //深度大于0.4
 89             if(depth > 0.4f)
 90                 //用一个较小的力往上推刚体
 91                 rigid.AddForce(force,ForceMode.Force);
 92             //深度小于等于0.4
 93             else
 94                 //用一个较大的力往上推刚体
 95                 rigid.AddForce(force * (depth * 2),ForceMode.Force);
 96         }
 97     }
 98 
 99     void OnTriggerExit(Collider other) {
100         //获取物体的刚体
101         Rigidbody r = other.GetComponent<Rigidbody>();
102         //有刚体
103         if(r) {
104             //是玩家且不受阻力水的阻力影响
105             if(r.tag == "Player" && !effectPlayerDrag)
106                 return;
107 
108             //阻力和角阻力字典包含刚体的游戏物体
109             if(dragStore.ContainsKey(r.gameObject) && angularStore.ContainsKey(r.gameObject)) {
110                 //恢复阻力
111                 r.drag = dragStore[r.gameObject];
112                 //恢复角阻力
113                 r.angularDrag = angularStore[r.gameObject];
114                 //移除阻力
115                 dragStore.Remove(r.gameObject);
116                 //移除角阻力
117                 angularStore.Remove(r.gameObject);
118             } else {
119                 //重置阻力
120                 r.drag = 0f;
121                 //重置角阻力
122                 r.angularDrag = 0.05f;
123                 print("Object left water: couldn't get drag values, restored to defaults");
124             }
125         }
126     }
127 }
Water

视频:https://pan.baidu.com/s/1mhF7hmo

项目:https://pan.baidu.com/s/1pL6nMTP

推荐阅读