Complete Physics Platformer Kit 学习
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
视频:https://pan.baidu.com/s/1mhF7hmo
项目:https://pan.baidu.com/s/1pL6nMTP