unity制作幽灵猎手射击游戏

news2024/11/25 20:28:50

文章目录

  • 介绍
  • 人物向着鼠标点击的位置跑动、旋转
  • 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");
		}
	}
}

给物体添加多个脚本,放入不同的预制体。

在这里插入图片描述
在这里插入图片描述


游戏结束动画

在这里插入图片描述
在这里插入图片描述






本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534418.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux-项目部署软件安装

安装jdk 操作步骤&#xff1a; 1、使用FinalShell自带的上传工具将jdk的二进制发布包上传到Linux jdk-8u171-linux-x64.tar.gz 2、解压安装包&#xff0c;命令为tar -zxvf jdk-8u171-linux-x64.tar.gz -C /usr/local 3、配置环境变量&#xff0c;使用vim命令修改/etc/profile文…

【JavaWeb】-- Filter、Listener、Ajax、Vue

文章目录 Filter1.概述2.快速入门2.1 开发步骤2.2 代码演示 3.Filter执行流程4. Filter拦截路径配置5.过滤器链5.1 概述5.2 代码演示5.3 问题 Listener1.概述2. 分类3.代码演示 Ajax1.概述1.1作用1.2 同步和异步 2.快速入门2.1 服务端实现2.2 客户端实现2.3 测试 3.axios3.1 基…

“Shell“Awk命令

文章目录 一.Awk二.Awk按行输出文本三.Awk按字段输出文本四.通过管道&#xff0c;双引号调用shell命令五.总结&#xff1a; 一.Awk Awk的工作原理&#xff1a; 逐行读取文本&#xff0c;默认以空格或tab键为分隔符进行分隔&#xff0c;将分隔所得的各个字段保存到内建变量中&a…

康耐视智能相机IS2000与三菱PLC走MC协议通讯设置详细步骤及案例详解

1.IS2000(SLMP扫描界面)设置如下: 控制器类型:PLC型号 IP地址:PLC地址 主机端口:16进制下的PLC通讯端口 2、IS2000设备寻址界面设置如下: 六大控制块存放区域根据PLC设定,偏移量及设备数可更改。3.打开GX WORKS软件,选择新建工程,点击“确定”,界面如下: 4.点击“连…

初识redis【redis的安装使用与卸载】

一.redis的概念 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。在redis官网中对redis的描述是这样的&#…

MADDPG-学习笔记(1)

文献链接&#xff1a;https://arxiv.org/abs/1706.02275 "Multi-Agent Actor-Critic for Mixed Cooperative-Competitive Environments"&#xff08;作者&#xff1a;Lowe, Ryan等人&#xff0c;2017年&#xff09; 环境搭建&#xff1a;https://zhuanlan.zhihu.co…

【保姆级教程】Windows安装CUDA及cuDNN

Windows安装CUDA及cuDNN 前言1. 第一次安装CUDA2. 第N次安装CUDA 一、 CUDA1. 查询CUDA版本2. 下载CUDA3. 安装CUDA4. 配置CUDA环境变量5. 检查CUDA是否安装成功 二、 cuDNN1. cuDNN版本的查询及下载2. 安装cuDNN3. 配置cuDNN的环境变量4. 检查cuDNN是否安装成功 三、查询CUDA及…

Linux常见指令理解

查看 Linux 主机 ip 在终端下敲 ssh[ip] ip 为刚才看到的 ifconfig 结果. 如果网络畅通, 将会提示输入用户名密码. 输入即可正确登陆 XShell 下的复制粘贴 复制: ctrl insert (有的 insert 需要配合 fn 来按) 粘贴: shift insert ctrl c / ctrl v 是不行的. Linu…

【C语言刷题——Leetcode10道简单题】

✨作者&#xff1a;平凡的人1 ✨专栏&#xff1a;《小菜鸟爱刷题》 ✨一句话&#xff1a;凡是过往&#xff0c;皆为序章 ✨说明: 过去无可挽回&#xff0c; 未来可以改变 今天是考四六级的日子&#xff0c;不知道大家考得怎么样呀&#xff1f;(反正我是寄了&#xff0c;好多没明…

Vue CLI ref props mixin plugin scoped

3.2. ref 属性 ref被用来给元素或子组件注册引用信息&#xff08;id的替代者&#xff09; 应用在html标签上获取的是真实DOM元素&#xff0c;应用在组件标签上获取的是组件实例对象vc使用方式 打标识&#xff1a;或获取&#xff1a;this.$refs.xxx <template> <di…

Python依据某一文件夹中大量文件的名称复制另一文件夹中的同名文件

本文介绍基于Python语言&#xff0c;针对一个文件夹下大量的Excel表格文件&#xff0c;基于其中每一个文件的名称&#xff0c;从另一个文件夹中找到与这一文件夹中文件同名的文件&#xff0c;并将找到的同名文件复制到第三个文件夹中的方法。 首先&#xff0c;我们来明确一下本…

【网络】深入浅出了解网络世界

HTTP协议 TCP/IP协议族 我们通常使用的网络&#xff0c;是在TCP/IP协议族上运作的。而HTTP协议就是TCP/IP内部的一个子集。 计算机与网络设备之间的通讯需要一定的规则&#xff0c;这种规则就称为协议。 TCP/IP就是互联网上各种协议的总称。 分层管理 TCP/IP 按层次分为…

架构活动中评估需求的五个关注点

从架构活动的整体目标出发&#xff0c;确认需求存在的必要性。很多时候&#xff0c;尤其是大的项目&#xff0c;需求方经常会夹带私货。虽然他们并没有什么恶意&#xff0c;但是这些附加的需求不仅会消耗研发资源&#xff0c;还会增加项目复杂度和规划难度。而最坏的情况&#…

【王道·计算机网络】第四章 网络层【未完】

一、 概述和功能 1.1 网络层功能 主要任务&#xff1a;把分组从源端传到目的端&#xff0c;为分组交换网上的不同主机提供通信服务传输单位&#xff1a;数据报功能&#xff1a; 路由选择与分组转发&#xff0c;即选择最佳路径异构网络互联(依靠路由器)拥塞控制(所有结点都来不…

会议日程——2023第十二届中国PMO大会

2023第十二届中国PMO大会 主题&#xff1a;拥抱变革 展现PMO力量 主办方&#xff1a;PMO评论 签到与入场&#xff1a;人脸识别 时间&#xff1a;6月17-18日 地点&#xff1a;北京蓝调庄园 【邀 请 函】 企业要基业长青就必须持续保持组织活力。企业的内外部环境不会一成不…

Java --- redis实现分布式锁

目录 一、锁的种类 二、分布式锁具备的条件与刚需 三、springbootredisngnix单机实现案例 四、Nginx配置负载均衡 4.1、修改nginx配置文件 4.2、执行启动命令 4.3、启动微服务程序测试 五、使用redis分布式锁 5.1、方法递归重试 5.2、自旋方式 5.3、添加key过期时间&#xff0…

Vue3如何按需引入Element Plus以及定制主题色

1.首先使用指令进行安装 npm install element-plus --save 2.安装按需引入另外两个插件 npm install -D unplugin-vue-components unplugin-auto-import 3.在vite.config.js文件引入以下内容 import { fileURLToPath, URL } from node:urlimport { defineConfig } from vite i…

CloudFlare系列--自定义CDN节点的IP

原文网址&#xff1a;CloudFlare系列--自定义CDN节点的IP_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CloudFlare如何手动指定CDN为CloudFlare的IP地址。 为什么手动指定CDN IP&#xff1f; 自选节点非常重要&#xff0c;原因如下&#xff1a; 国内访问不同CDN节点的速…

Linux vim光标移动/退出命令/撤退操作/文本查找 等命令大全

1 什么是vim&#xff1f; vim是Linux环境下一款强大、高度可定制的文本编辑工具。能够编辑任何的ASCII格式文件&#xff0c;对内容进行创建、查找、替换、修改、删除、复制、粘贴等操作。编写文件时&#xff0c;无需担心目标文件是否存在&#xff0c;若不存在则会自动在内存中…

老Q魔改MACD:拒绝大幅回撤,威力比原版强太多了!

看过老Q历史文章的股友都知道,MACD是一个非常经典且依旧奋战在第一线的顶流指标。我们之前也目前主流通用的参数版本在沪深300上做了回测,17年来获得了累计365%的收益。 然而,整个沪深300大盘在这17年里也涨了超过300%,也就是说,我们的策略也仅仅比拿着不动好上一丢丢而已…