文章目录
- 介绍
- 人物向着鼠标点击的位置跑动、旋转
- lerp函数让摄像机平滑跟随
- 敌人导航
- 敌人攻击
- 发射子弹攻击敌人
- 玩家健康
- 敌人健康
- 分数显示
- 刷怪笼
- 游戏结束动画
介绍
玩家鼠标控制人物转向
玩家鼠标点击控制光线发射的终点
玩家受到伤害屏幕闪红
有三个怪物生成点
玩家射杀敌人获得分数
关键技术:动画器、屏幕射线检测负责转向、枪口粒子特效、枪口灯光、屏幕射线检测负责发射光线、line renderer、lerp函数相机移动、颜色lerp渐变
人物向着鼠标点击的位置跑动、旋转
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class PlayerMovement : MonoBehaviour
{
public float speed = 6f; // 玩家移动速度
private Vector3 movement; // 玩家的移动方向
private Animator playerAC; // 玩家的动画控制器
private Rigidbody playerRigidbody; // 玩家的刚体组件
LayerMask floorMask;
// 初始化
void Start()
{
// 获取动画控制器和刚体组件
playerAC = GetComponent<Animator>();
playerRigidbody = GetComponent<Rigidbody>();
floorMask = LayerMask.GetMask("floor");
}
// 固定时问见新
void FixedUpdate()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
// 移动 横向 和纵向
Move(h, v);
// 检测是否在移动,播放相应动画
Animating(h, v);
turning();
}
// 检测是否在移动,播放相应动画
void Animating(float h, float v)
{
// 只有h不等于0或者v不等于0才应该是移动
bool walking = h != 0f || v != 0f;
playerAC.SetBool("iswalking", walking);
}
// 移动
void Move(float h, float v)
{
// 设置移动的方向向量
movement.Set(h, 0f, v);
movement = movement.normalized * speed * Time.deltaTime;
// 使用Rigidbody组件移动玩家
playerRigidbody.MovePosition(transform.position + movement);
}
void turning()
{
Ray cameraRay = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit cameraHit;
if (Physics.Raycast(cameraRay, out cameraHit, 100f, floorMask))
{
Vector3 playerToMouse = cameraHit.point - transform.position;
playerToMouse.y = 0f;
Quaternion newQuaternion = Quaternion.LookRotation(playerToMouse);
playerRigidbody.MoveRotation(newQuaternion);
}
}
}
void Start()
此函数在脚本开始运行时被调用。
它获取了动画控制器(playerAC)和刚体组件(playerRigidbody)。
设置了地面层的遮罩(floorMask)。
void FixedUpdate()
此函数在固定的时间间隔内被调用,用于物理相关的更新。
获取水平轴(h)和垂直轴(v)的输入值。
调用Move(h, v)函数进行移动。
调用Animating(h, v)函数根据移动状态播放相应的动画。
调用turning()函数根据鼠标位置旋转玩家角色。
void Animating(float h, float v)
检测是否在移动,并根据移动状态设置动画参数。
当h或v不等于0时,设置iswalking布尔参数为true,表示正在移动。
void Move(float h, float v)
设置移动方向向量movement。
使用标准化后的移动方向向量乘以速度和时间间隔,得到移动的位移向量。
使用刚体组件(playerRigidbody)将玩家位置进行移动。
void turning()
创建一条从主摄像机通过鼠标位置的射线(cameraRay)。
使用射线与地面层遮罩(floorMask)进行碰撞检测,并获取碰撞结果(cameraHit)。
计算玩家角色指向鼠标位置的向量(playerToMouse)。
将向量的y分量设为0,以保持在水平平面上旋转。
创建一个新的四元数(newQuaternion),表示玩家角色旋转的目标方向。
使用刚体组件(playerRigidbody)将玩家角色的旋转进行平滑插值。
lerp函数让摄像机平滑跟随
using UnityEngine;
using System.Collections;
public class CameraFollow : MonoBehaviour {
public Transform target;
public float smoothing = 5f;
Vector3 offset;
// Use this for initialization
void Start () {
offset = transform.position - target.position;
}
// Update is called once per frame
void Update () {
Vector3 pos = target.position + offset;
transform.position = Vector3.Lerp(transform.position, pos, smoothing * Time.deltaTime);
}
}
它通过Lerp方法将相机的位置平滑地移动到目标物体的位置。
首先,计算新的相机位置pos,该位置是目标物体的位置加上初始偏移量offset。
然后,使用Vector3.Lerp方法将相机当前的位置(transform.position)与新位置pos之间进行线性插值,以实现平滑过渡。
插值的速度由smoothing变量和Time.deltaTime控制。
敌人导航
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
public class EnemyMovement : MonoBehaviour {
Transform player; // 目标位置:英雄
NavMeshAgent nav; // 导航代理
EnemyHealth enemyHealth;
Playerhealth playerHealth;
// Use this for initialization
void Start () {
player = GameObject.FindGameObjectWithTag("Player").transform;
nav = GetComponent<NavMeshAgent>();
enemyHealth=GetComponent<EnemyHealth>();
playerHealth=player.GetComponent<Playerhealth>();
}
// Update is called once per frame
void Update () {
if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
nav.SetDestination(player.position);
else{
nav.enabled=false;
}
}
}
Start()函数:初始化敌人角色的位置和导航代理,并获取敌人和英雄角色的健康组件。
Update()函数:如果敌人和英雄角色的健康状态都大于0,使用导航代理将敌人角色移动到英雄角色的位置;否则,禁用导航代理停止敌人角色的移动。
敌人攻击
using System.Collections;
using UnityEngine.UI;
using UnityEngine;
public class EnemyAttack : MonoBehaviour {
Playerhealth playerHealth;
GameObject player;
public int attack=10;
public float attackBetweenTime=0.5f;
float timer;
public AudioSource playerAudio;
bool inRange;
void Start () {
playerAudio = GetComponent<AudioSource>();
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<Playerhealth>();
}
void Update () {
// 如果收到伤害,显示受伤闪烁效果
timer+=Time.deltaTime;
// 如果在攻击范围内,执行攻击逻辑
if (timer > attackBetweenTime&&inRange)
{
Attack();
}
}
void OnTriggerEnter(Collider other) {
// 如果检测到玩家进入攻击范围,设置 inRange 标志为 true
if (other.gameObject == player) {
inRange = true;
}
}
void OnTriggerExit(Collider other) {
// 如果检测到玩家离开攻击范围,设置 inRange 标志为 fals
if (other.gameObject == player) {
inRange = false;
}
}
void Attack() {
timer=0;
if (playerHealth.currentHealth > 0) {
playerHealth.OnAttack(attack);
}
}
void Die() {
// 在此添加死亡的相关逻辑,例如播放死亡动画、停止移动等
}
}
Start()函数:获取玩家的健康组件和游戏对象,并初始化音频源。
Update()函数:检测计时器,并在攻击范围内执行攻击逻辑。
OnTriggerEnter(Collider other)函数:当检测到玩家进入攻击范围时,设置inRange标志为true。
OnTriggerExit(Collider other)函数:当检测到玩家离开攻击范围时,设置inRange标志为false。
Attack()函数:执行攻击逻辑,重置计时器,并检查玩家是否存活,然后对玩家进行攻击。
发射子弹攻击敌人
using UnityEngine;
using System.Collections;
public class PlayerShooting : MonoBehaviour
{
AudioSource gunAudio;
Light gunLight;
LineRenderer gunLine;
Ray gunRay;
RaycastHit gunHit;
public LayerMask layerMask;
ParticleSystem gunParticies;
float timer;
public int atk=20;
void Start()
{
gunAudio = GetComponent<AudioSource>();
gunLight = GetComponent<Light>();
gunLine = GetComponent<LineRenderer>();
// layerMask = LayerMask.GetMask("shoot");
gunParticies=GetComponent<ParticleSystem>();
}
void Update()
{
timer+=Time.deltaTime;
if (Input.GetButtonDown("Fire1"))
{
shoot();
}
if (timer>0.1f)
{
timer=0;
gunLine.enabled=false;
gunLight.enabled=false;
}
}
void shoot() {
//鼓
gunAudio.Play();
//光源
gunLight.enabled = true;
//枪
gunLine.enabled = true;
gunRay.origin = transform.position;
//gunRay.direction = transform.forward;
gunRay.direction = transform.TransformDirection(Vector3.forward);
//检的第一个点
gunLine.SetPosition(0, transform.position);
gunParticies.Stop();
gunParticies.Play();
//判断是否击中敌人
if (Physics.Raycast(gunRay, out gunHit, 100f, layerMask)) {
EnemyHealth enemyHealth = gunHit.collider.GetComponent<EnemyHealth>();
if (enemyHealth != null)
{
Debug.Log("打到Zombunny");
// 在这里处理击中敌人的逻辑
enemyHealth.OnAttack(atk);
}
gunLine.SetPosition(1, gunHit.point);
//在这里处理击中敌人的逻辑
}
else {
gunLine.SetPosition(1, transform.position + gunRay.direction* 100f);
}
}
}
Start()函数:初始化枪声音频源、枪光源、射线渲染器和粒子系统。
Update()函数:更新计时器,并在按下"Fire1"键时触发射击逻辑。
shoot()函数:处理射击逻辑,播放枪声音效、启用枪光源和射线渲染器,发射射线进行击中检测。如果击中敌人,则处理敌人受伤逻辑,并在射线上显示击中点。如果没有击中敌人,则在射线上显示射程范围内的终点。
玩家健康
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Playerhealth : MonoBehaviour
{
public int startingHealth = 100;
public int currentHealth;
public Slider slider;
public Image hurtImage;
bool isDamage;
private AudioSource playerAudio;
public AudioClip deadclip;
public Color flashColor = new Color(1f, 0f, 0f, 1f);
public Color clearColor = Color.clear;
Animator anim;
PlayerMovement playermove;
// Use this for initialization
void Start()
{
playerAudio = GetComponent<AudioSource>();
currentHealth = startingHealth;
anim=GetComponent<Animator>();
playermove=GetComponent<PlayerMovement>();
}
// Update is called once per frame
void Update()
{
if (isDamage)
hurtImage.color = flashColor;
else
{
hurtImage.color = Color.Lerp(hurtImage.color,clearColor,Time.deltaTime*5);
}
isDamage = false;
// 检查当前生命值是否小于等于 0
}
// 受伤方法
public void OnAttack(int damage)
{
isDamage=true;
// 减少生命值
currentHealth -= damage;
// 更新滑动条的值
slider.value = currentHealth;
// 播放受伤音效
playerAudio.Play();
if (currentHealth <= 0)
{
// 如果生命值小于等于 0,则触发死亡事件
Die();
}
}
// 死亡方法
void Die()
{
playerAudio.clip=deadclip;
playerAudio.Play();
anim.SetTrigger("die");
playermove.enabled=false;
}
}
Start()函数:初始化玩家的血量、音频源、动画组件和移动组件。
Update()函数:更新受伤图像的颜色,将其逐渐恢复到透明,重置受伤标志。
OnAttack(int damage)函数:处理玩家受到攻击时的逻辑,减少生命值、更新滑动条的值、播放受伤音效,并在生命值小于等于0时触发死亡逻辑。
Die()函数:处理玩家死亡时的逻辑,播放死亡音效,触发死亡动画,禁用移动组件。
敌人健康
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
public class EnemyHealth : MonoBehaviour
{
// 初始血量
public int startingHealth = 50;
// 当前血量
public int currentHealth;
// 敌人音频源
AudioSource enemyAudioSource;
public AudioClip enermyclip;
bool isdie;
Animator anim;
// 初始化
void Start()
{
currentHealth = startingHealth;
enemyAudioSource = GetComponent<AudioSource>();
anim=GetComponent<Animator>();
}
// 更新方法,每帧调用一次
void Update()
{
}
// 受伤方法
public void OnAttack(int damage)
{
// 减少血量
currentHealth -= damage;
// 播放音效
enemyAudioSource.Play();
if (currentHealth<0&&!isdie)
{
Dead();
}
}
void Dead(){
isdie =true;
anim.SetTrigger("dead");
gameObject.GetComponent<NavMeshAgent>().enabled = false;
gameObject.GetComponent<EnemyMovement>().enabled = false;
enemyAudioSource.clip=enermyclip;
enemyAudioSource.Play();
Destroy(this.gameObject,1.1f);
ScoreManager.score+=10;
}
}
Start()函数:初始化敌人的血量、音频源和动画组件。
OnAttack(int damage)函数:处理敌人受到攻击时的逻辑,减少血量、播放音效,并在血量为零时执行死亡逻辑。
Dead()函数:处理敌人死亡时的逻辑,触发死亡动画、禁用导航代理和敌人移动组件,播放死亡音效,延迟一定时间后销毁敌人游戏对象,增加得分。
分数显示
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public static int score;
Text text;
void Start()
{
text = GetComponent<Text>();
}
void Update()
{
text.text = "SCORE: " + score;
}
}
刷怪笼
using UnityEngine;
using System.Collections;
public class GameOverManager : MonoBehaviour {
public Playerhealth playerHealth;
Animator anim;
public GameObject PLAYER;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
playerHealth=PLAYER.GetComponent<Playerhealth>();
}
// Update is called once per frame
void Update () {
if(playerHealth.currentHealth <= 0) {
anim.SetTrigger("GameOver");
}
}
}
给物体添加多个脚本,放入不同的预制体。
游戏结束动画