【Unity】3D功能开发入门系列(五)

news2024/9/22 7:42:31

Unity3D功能开发入门系列(五)

  • 一、预制体
    • (一)预制体
    • (二)预制体的创建
    • (三)预制体实例
    • (四)预制体的编辑
  • 二、动态创建实例
    • (一)动态创建实例
    • (二)实例的初始化
    • (三)实例的销毁
    • (四)练习:火控参数&按键控制
  • 三、物理系统
    • (一)物理系统
    • (二)物理碰撞
    • (三)反射与摩擦
  • 四、碰撞检测
    • (一)运动学刚体
    • (二)碰撞检测
    • (三)碰撞体的编辑
    • (四)练习:击毁目标
  • 五、游戏项目实战 — 射击小游戏

一、预制体

(一)预制体

预制体 Prefab,即预先制作好的物体。使用预制体,可以快速创建物体,提高开发效率。

演示:导出 RacingCar 资源包

  • 在 Prefab 目录下,是预制体资源,* .prefab
  • 用预制体来构造物体
    在这里插入图片描述

在 Prefab 中,多级节点 / 父子关系,是常见的情况

(二)预制体的创建

  1. 先制作好一个样本节点
  2. 做好以后,直接拖到 Assets 窗口,则自动生成一个 *.prefab 资源
  3. 原始物体不再需要,可以删除
    在这里插入图片描述

导出 prefab 资源时,应将依赖文件一并导出prefab 只是记录了节点信息,文件中不包含材质、贴图数据,仅包含引用在这里插入图片描述

(三)预制体实例

1. prefab Instance,由预制体创建得到的对象
特征: 在 Hierarchy 中,节点图标不同、右键菜单 | Prefab、上下文工具 | Prefab
在这里插入图片描述
在这里插入图片描述
2. Prefab Instance 和原 Prefab 之间存在关联
右键菜单 Prefab | Unpack,则解除关联,成为普通物体
在这里插入图片描述

在这里插入图片描述

(四)预制体的编辑

*.prefab 相当于是一个模板,可以再次编辑(其实例也会同步改变)

1. 单独编辑

  • 双击 Prefab,进入 单独编辑 模式
  • 编辑节点和组件
  • 退出,完成编辑
    在这里插入图片描述

2. 原位编辑

  • 选择 Prefab Instance
  • 在检查器中 Open
  • Context 显示:Normal / Gray / Hidden (此时,仅选中的物体被编辑,其余物体是陪衬
  • 编辑节点
  • 退出,完成编辑
    在这里插入图片描述
    在这里插入图片描述

3. 覆盖编辑

  • 选择 Prefab Instance
  • 直接在场景中编辑
  • 编辑完后 Override | Apply 应用编辑、Override | Revert 取消编辑
    在这里插入图片描述

二、动态创建实例

(一)动态创建实例

创建 Prefab 之后,用 API 动态创建实例

Object.Instance(original,parent);

演示:

  1. 准备子弹 prefab
  2. 添加火控脚本 FireLogic
  • 添加变量
public GameObject bulletPrefab;
  • 克隆实例
GameObject node = Instance(bulletPrefab,null);
node.transform.position = Vector3.zero;
node.tansform.localEulerAngles = Vector3.zero;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireLogic : MonoBehaviour
{
    // 对子弹 prefab 资源的引用
    public GameObject bulletPrefab;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            TestFire();
        }
    }

    private void TestFire()
    {
        Debug.Log("* 创建子弹的实例 ..");
        GameObject node = Object.Instantiate(bulletPrefab, null);
        node.transform.position = Vector3.zero;
        node.transform.localEulerAngles = Vector3.zero;
    }
}

在这里插入图片描述

(二)实例的初始化

创建 Prefab Instance 之后,应做初始化:
1. parent,设置父节点。 为便于节点的管理,应单独创建一个父节点
在这里插入图片描述

2. position / localPosition,出生节点。 为便于操作,应显示标记一个出生点
在这里插入图片描述
3. eulerAngles / localEulerAngles / rotation 旋转。 对子弹来说,子弹角度应与炮塔旋转角度一致
在这里插入图片描述

4. Script,自带的控制脚本。 子弹自带了 BulletLogic,可以控制其飞行速度
在这里插入图片描述

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletLogic : MonoBehaviour
{
    public float speed;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(0, 0, speed, Space.Self);
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireLogic : MonoBehaviour
{
    // 对子弹 prefab 资源的引用
    public GameObject bulletPrefab;
    // 父子关系靠 Transform 维持,后面创建实例要找父级
    public Transform bulletFolder;
    // 出生点位置的指定
    public Transform firePoint;
    // 得到炮塔的旋转角度
    public Transform cannon;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            TestFire();
        }
    }

    private void TestFire()
    {
        Debug.Log("* 创建子弹的实例 ..");
        GameObject node = Object.Instantiate(bulletPrefab, bulletFolder);
        node.transform.position = this.firePoint.position; //Vector3.zero;
        node.transform.localEulerAngles = this.cannon.eulerAngles; //Vector3.zero;
        // node.transform.rotation = this.cannon.rotation;

        BulletLogic script = node.GetComponent<BulletLogic>();
        script.speed = 0.5f;
    }
}

  • 一般引用 Transform,而 GameObject 是没有存在感的
  • 可以使用空物体,标记一个空间位置

(三)实例的销毁

一般的,创建实例之后,也要负责销毁。

对于子弹来说

  • 飞出屏幕时,销毁 X
  • 射程 / 飞行时间 V
  • 击中目标时,销毁 V

Object.Destroy( obj ),用于销毁一个实例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletLogic : MonoBehaviour
{
    public float speed;

    public float maxDistance;
    // Start is called before the first frame update
    void Start()
    {
        float lifetime = 1;
        if(speed > 0)
        {
            lifetime = maxDistance / speed;
        }
        Invoke("SelfDestroy", lifetime);
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(0, 0, speed, Space.Self);
    }

    private void SelfDestroy()
    {
        Debug.Log("* 自毁 !");
        Object.Destroy(this.gameObject);
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireLogic : MonoBehaviour
{
    // 对子弹 prefab 资源的引用
    public GameObject bulletPrefab;
    // 父子关系靠 Transform 维持,后面创建实例要找父级
    public Transform bulletFolder;
    // 出生点位置的指定
    public Transform firePoint;
    // 得到炮塔的旋转角度
    public Transform cannon;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            TestFire();
        }
    }

    private void TestFire()
    {
        Debug.Log("* 创建子弹的实例 ..");
        GameObject node = Object.Instantiate(bulletPrefab, bulletFolder);
        node.transform.position = this.firePoint.position; //Vector3.zero;
        node.transform.localEulerAngles = this.cannon.eulerAngles; //Vector3.zero;
        // node.transform.rotation = this.cannon.rotation;

        // 指定初始飞行速度
        BulletLogic script = node.GetComponent<BulletLogic>();
        script.speed = 0.5f;

        script.maxDistance = script.speed * 10;
    }
}

在这里插入图片描述

  • Destroy( this ) 这样写是错误的,我们要销毁物体,而不是组件
  • Destroy( ) 不会立即执行,而是在本轮 Update 之后才执行

(四)练习:火控参数&按键控制

练习1:火控参数的完善

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireLogic : MonoBehaviour
{
    // 对子弹 prefab 资源的引用
    public GameObject bulletPrefab;
    // 父子关系靠 Transform 维持,后面创建实例要找父级
    public Transform bulletFolder;
    // 出生点位置的指定
    public Transform firePoint;
    // 得到炮塔的旋转角度
    public Transform cannon;
    // 实现火控参数的动态管理
    public float bulletSpeed;
    public float bulletLifetime;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            TestFire();
        }
    }

    private void TestFire()
    {
        Debug.Log("* 创建子弹的实例 ..");
        GameObject node = Object.Instantiate(bulletPrefab, bulletFolder);
        node.transform.position = this.firePoint.position; //Vector3.zero;
        node.transform.localEulerAngles = this.cannon.eulerAngles; //Vector3.zero;
        // node.transform.rotation = this.cannon.rotation;

        // 指定初始飞行速度
        BulletLogic script = node.GetComponent<BulletLogic>();
        script.speed = this.bulletSpeed;
        script.maxDistance = script.speed * this.bulletLifetime;
    }
}

在这里插入图片描述

练习2:添加按键控制,旋转炮塔方向
官方文档:https://docs.unity.cn/cn/2022.3/ScriptReference/Transform-localEulerAngles.html,不建议使用rotate
在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireLogic : MonoBehaviour
{
    // 对子弹 prefab 资源的引用
    public GameObject bulletPrefab;
    // 父子关系靠 Transform 维持,后面创建实例要找父级
    [Tooltip("子弹节点管理")]
    public Transform bulletFolder;
    [Tooltip("子弹出生点")]
    public Transform firePoint;
    // 得到炮塔的旋转角度
    [Tooltip("炮塔")]
    public Transform cannon;
    // 实现火控参数的动态管理
    [Tooltip("子弹飞行速度")]
    public float bulletSpeed;
    [Tooltip("子弹飞行时长")]
    public float bulletLifetime;
    // 实现连发 + 方向控制
    [Tooltip("子弹发射间隔")]
    public float bulletInterval;
    [Tooltip("炮塔转速")]
    public float rotateSpeed;

    // 当前转角
    private Vector3 m_eulerAngles;

    void Start()
    {
        StartFire();
    }

    void Update()
    {
        float delta = rotateSpeed * Time.deltaTime;

        // 左右是绕 y 轴旋转,上下是绕 x 轴旋转(上左负)
        if(Input.GetKey(KeyCode.A))
        {
            //左转
            if (m_eulerAngles.y > -75)
                m_eulerAngles.y -= delta;
        }
        if(Input.GetKey(KeyCode.D))
        {
            //右转
            if (m_eulerAngles.y < 75)
                m_eulerAngles.y += delta;
        }
        if(Input.GetKey(KeyCode.W))
        {
            //上转
            if(m_eulerAngles.x > -60)
                m_eulerAngles.x -= delta;
        }
        if(Input.GetKey(KeyCode.S))
        {
            if(m_eulerAngles.x < 10)
                m_eulerAngles.x += delta;
        }

        cannon.transform.localEulerAngles = m_eulerAngles;
    }

    private void TestFire()
    {
        // 指定父节点
        GameObject node = Object.Instantiate(bulletPrefab, bulletFolder);
        // 指定出生点
        node.transform.position = this.firePoint.position; 
        // 与炮塔角度一致
        node.transform.localEulerAngles = this.cannon.eulerAngles; 
        
        // 指定初始飞行速度
        BulletLogic script = node.GetComponent<BulletLogic>();
        script.speed = this.bulletSpeed;
        script.maxDistance = script.speed * this.bulletLifetime;
    }

    // 开火
    public void StartFire()
    {
        if(!IsInvoking("TestFire"))
        {
            InvokeRepeating("TestFire", bulletInterval, bulletInterval);
        }
    }
    public void StopFire()
    {
        CancelInvoke("TestFire");
    }
}

在这里插入图片描述


三、物理系统

(一)物理系统

  1. 物理系统 Physics,即由物理规律起作用的系统,确切的说,是牛顿运动定律(力、质量、速度)
    演示:布置一个场景,添加 ‘地面’,‘苹果’,运行游戏,‘苹果’ 悬空,这很不牛顿!

  2. 刚体组件 Rigidbody,物理学中的物体,当添加 Rigidbody 后,由 物理引擎负责刚体运动(物理引擎会根据它的质量、力、速度之间的关系自动做出运算,使其运动)

给 ‘苹果’ 添加一个 Rigidbody 组件:Physics | Rigidbody,运行游戏
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(二)物理碰撞

物理系统,不仅接管了刚体的运动,也接管了碰撞
碰撞体 Collider,描述了物体的碰撞范围。其中 Box Collider 长方碰撞体、Sphere Collider 球形碰撞体
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
绿框与绿框相碰,会被物理引擎捕捉到
在这里插入图片描述

(三)反射与摩擦

钢铁的反弹与摩擦,也归物理系统负责。
演示:

  • 新建 Physical Material,添加给小球
    在这里插入图片描述
    在这里插入图片描述
  • 设置 Friction、Bounciness
  • 观察小球的反弹
    在这里插入图片描述

四、碰撞检测

(一)运动学刚体

运动学刚体 Kinematic,即质量为 0 的刚体。由于质量为 0,所以此刚体不受牛顿约束。
在这里插入图片描述
此时,需要使用脚本使运动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleLogic : MonoBehaviour
{
    public Vector3 speed;
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(speed * Time.deltaTime, Space.Self);
    }
}

(二)碰撞检测

对于运动学刚体,也支持碰撞检测。由 物理引擎 负责检测。
在这里插入图片描述
演示:

  1. 勾上 Rigidbody | Is Kinematic、Collider | Is Trigger(触发器)
  2. 挂一个脚本,添加消息函数
void OnTriggerEnter(Collider other){}

其中,Collider other 对方碰撞体、other.gameObject 对方节点、other.name 对方节点的名字

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SimpleLogic : MonoBehaviour
{
    public Vector3 speed;
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(speed * Time.deltaTime, Space.Self);
    }

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("* 发生了碰撞,other=" + other.name);
    }
}

在这里插入图片描述

  1. 物理引擎 只负责探测 ( Trigger ) ,不会阻止物体或者反弹
  2. 物体引擎 计算的是 Collider 之间的碰撞,和物体自身形状无关
  3. 当检测到碰撞时,会调用 当前节点脚本中的 OnTriggerEnter 消息

(三)碰撞体的编辑

碰撞体 Collider 的形状,规定了碰撞的边界。其形状是可以编辑的:

  • Box Collider 盒形:
    Center 中心位置,相当于物体的轴心点;Size 长宽高。点 Edit Collider,可以直接编辑绿色框
  • Sphere Collider 球形:
    Center 中心位置,相当于物体的轴心点;Radius 半径大小。点 Edit Collider,可以直接编辑绿色框

练习:添加子弹物体

  1. 检查原先有没有碰撞体,如果有,则先移除
  2. 根据体型,选择合适形状的碰撞体:此处,添加一个 Box Collider
  3. 编辑碰撞体,调整边界:一般无需调整,Unity会自动创建合适的尺寸
    在这里插入图片描述

碰撞体的范围,不用太精确,大致覆盖物体即可

(四)练习:击毁目标

练习:发射一发子弹,击毁目标

  1. 运动学刚体设置 Rigidbody | Is Kinematic
  2. 触发器模式设置 Collider | Is Trigger
  3. 消息函数 void OnTriggerEnter()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletLogicDistruct : MonoBehaviour
{
    public Vector3 speed;
    
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.Translate(speed * Time.deltaTime, Space.Self);
    }

    private void OnTriggerEnter(Collider other)
    {
        if(other.name.Equals("目标"))
        {
            Debug.Log("* 子弹发生碰撞,other=" + other.name);
            Object.Destroy(other.gameObject);
            Object.Destroy(this.gameObject);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MainLogic : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Application.targetFrameRate = 60;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

在这里插入图片描述

碰撞的双方,只需一方设置为运动学刚体即可


五、游戏项目实战 — 射击小游戏

制作一个射击游戏
海空背景、玩家、子弹(无限数量)、怪兽(蛇皮走位,无限数量)、子弹特效(爆炸特效)、背景音乐

(1) 添加两个角色(预制体)玩家、敌人

在这里插入图片描述

(2)天空盒 Skybox,即游戏的背景
Windows | Rendering | Lighting,光照设置 —> Environment | Skybox Material,天空盒材质
在这里插入图片描述
在这里插入图片描述

调整摄像头视角
在这里插入图片描述
在这里插入图片描述

(3) 子弹和碰撞
添加子弹、子弹脚本、碰撞检测
在这里插入图片描述
在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiBulletLogic : MonoBehaviour
{
    [Tooltip("子弹飞行速度")]
    public float speed = 1;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0, speed * Time.deltaTime, Space.Self);
    }
}

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiBulletLogic : MonoBehaviour
{
    [Tooltip("子弹飞行速度")]
    public float speed = 1;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0, speed * Time.deltaTime, Space.Self);
    }

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("* 子弹碰撞,other=" + other.name);
    }
}

  1. 怪兽是一个空物体,其碰撞体要手动编辑
    在这里插入图片描述

在这里插入图片描述

  1. 主控脚本中帧率的设置(让CPU压力小一点)
    在这里插入图片描述
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiMainLogic : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Application.targetFrameRate = 60;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

(4)连续发射

1. 子弹

  • 增加自毁时间 lifetime
  • 把子弹做成 prefab
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiBulletLogic : MonoBehaviour
{
    [Tooltip("子弹飞行速度")]
    public float speed = 1;
    [Tooltip("子弹生命时长")]
    public float lifetime = 3;

    // Start is called before the first frame update
    void Start()
    {
        Invoke("SelfDestroy", lifetime);
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0, speed * Time.deltaTime, Space.Self);
    }

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("* 子弹碰撞,other=" + other.name);

        if (!other.name.StartsWith("怪兽")) return;

        Destroy(this.gameObject);
        Destroy(other.gameObject);
    }

    private void SelfDestroy()
    {
        Object.Destroy(this.gameObject);
    }
}

在这里插入图片描述

2. 玩家

  • 定义发射点 fire point
  • 定义子弹目录 bullet folder
  • 使用定时器,子弹连发

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

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiPlayerLogic : MonoBehaviour
{
    [Tooltip("子弹节点的预制体")]
    public GameObject bulletPrefab;
    [Tooltip("子弹节点的父节点")]
    public Transform bulletFolder;
    [Tooltip("子弹出生点")]
    public Transform firePoint;
    [Tooltip("开火间隔")]
    public float fireInterval = 0.5f;
    
    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("Fire", fireInterval, fireInterval);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void Fire()
    {
        // 实例化一个子弹节点
        GameObject node = Instantiate(bulletPrefab, bulletFolder);
        // 调整子弹发射点位置
        node.transform.position = firePoint.position;
    }
}

(5)按键控制:添加按键控制,让玩家左右移动

using System.Collections;
using System.Collections.Generic;
using System.Data.SqlTypes;
using UnityEngine;

public class LiPlayerLogic : MonoBehaviour
{
    [Tooltip("子弹节点的预制体")]
    public GameObject bulletPrefab;
    [Tooltip("子弹节点的父节点")]
    public Transform bulletFolder;
    [Tooltip("子弹出生点")]
    public Transform firePoint;
    [Tooltip("开火间隔")]
    public float fireInterval = 0.5f;
    [Tooltip("平移速度")]
    public float moveSpeed = 0.3f;
    
    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("Fire", fireInterval, fireInterval);
    }

    // Update is called once per frame
    void Update()
    {
        float dx = 0;

        if (Input.GetKey(KeyCode.A))
            dx = -moveSpeed;
        if (Input.GetKey(KeyCode.D))
            dx = moveSpeed;

        this.transform.Translate(dx, 0, 0, Space.Self);
    }

    private void Fire()
    {
        // 实例化一个子弹节点
        GameObject node = Instantiate(bulletPrefab, bulletFolder);
        // 把子弹移动到出生点位置
        node.transform.position = firePoint.position;
    }
}

在这里插入图片描述

(6)怪兽控制:给怪兽添加脚本 EnemyLogic

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiEnemyLogic : MonoBehaviour
{
    [Tooltip("前进速度")]
    public float zSpeed = 10;
    // 横移速度
    float xSpeed = 0;
    
    // Start is called before the first frame update
    void Start()
    {
        // 每秒改变一次横移速度
        InvokeRepeating("SnakeMove", 1f, 1f);
    }

    // Update is called once per frame
    void Update()
    {
        float dz = zSpeed * Time.deltaTime;
        float dx = xSpeed * Time.deltaTime;

        this.transform.Translate(dx, 0 , dz, Space.Self);
    }

    // 蛇皮走位
    void SnakeMove()
    {
        // 4 种速度选项
        float[] options = { -10, -5, 5, 10 };
        int sel = Random.Range(0, options.Length);
        xSpeed = options[sel];
    }
}

(7)怪兽生成器:定时生成怪兽(实质还是预制体创建实例的技术)

更新预制体的资源,隐藏怪兽
在这里插入图片描述
在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiEnemyCreator : MonoBehaviour
{
    public GameObject enemyPrefab;

    public float interval = 1;

    // Start is called before the first frame update
    void Start()
    {
        InvokeRepeating("CreateEnemy", 0.1f, interval);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void CreateEnemy()
    {
        GameObject node = Instantiate(enemyPrefab, this.transform);
        node.transform.position = this.transform.position;
        // 把怪兽的投头转过来
        node.transform.localEulerAngles = new Vector3(0, 180, 0);
        // 出生之后,不要在原点呆着,而是左右偏一个位置,防止被立即击毙
        float dx = Random.Range(-30, 30);
        node.transform.Translate(dx, 0, 0, Space.Self);
    }
}

在这里插入图片描述
(8)子弹特效:添加子弹特效,粒子特效 Particle System

注意,修改之后要应用 Prefab

在这里插入图片描述

(9)爆炸特效 :在击中目标时,创建特效节点
在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LiBulletLogic : MonoBehaviour
{
    [Tooltip("子弹飞行速度")]
    public float speed = 25;
    [Tooltip("子弹生命时长")]
    public float lifetime = 3;
    [Tooltip("爆炸粒子特效的Prefab")]
    public GameObject explosionEffect;

    // Start is called before the first frame update
    void Start()
    {
        Invoke("SelfDestroy", lifetime);
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0, speed * Time.deltaTime, Space.Self);
    }

    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("* 子弹碰撞,other=" + other.name);

        if (!other.name.StartsWith("怪兽")) return;

        Destroy(this.gameObject);
        Destroy(other.gameObject);

        // 创建一个粒子特效,表现爆炸的效果
        GameObject effectNode = Instantiate(explosionEffect, null);
        effectNode.transform.position = this.transform.position;
        // 当粒子特效播放完,会自己销毁
    }

    private void SelfDestroy()
    {
        Object.Destroy(this.gameObject);
    }
}

在这里插入图片描述

最后再设置一下音乐
在这里插入图片描述


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

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

相关文章

2024/8/4 汇川变频器低压产品分类选型

VF就是通过电压、频率控制 矢量就是通过开环&#xff08;svc&#xff09;和闭环&#xff08;fvc&#xff09; MD310、MD200 开环&#xff0c;不支持闭环&#xff0c;无法接编码器 290 、200s、280、都是VF控制

有哪些供应链管理方法?详解四种常用的供应链管理方法!

在当今复杂多变的商业环境中&#xff0c;供应链管理已成为企业获取竞争优势的关键。有效的供应链策略不仅能提升企业的响应速度和市场适应性&#xff0c;还能显著降低成本、提高效率。本文将深入探讨几种主流的供应链管理方法&#xff0c;包括快速反应、有效客户反应、基于活动…

LeetCode 0572.另一棵树的子树:深搜+广搜(n^2做法就能过,也有复杂度耕地的算法)

【LetMeFly】572.另一棵树的子树&#xff1a;深搜广搜&#xff08;n^2做法就能过&#xff0c;也有复杂度耕地的算法&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/subtree-of-another-tree/ 给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 s…

DEBUG:sw模板不对

问题 sw自带模板不合适 解决 工具 选项 文件位置 自己新建一个文件夹 放入模板 &#xff08;三维 二维各一个 一般就是统一标准 可以自己新建个模板&#xff09;

深度学习笔记(神经网络+VGG+ResNet)

深度学习 主要参考博客常用英语单词 概念应用神经网络基础神经网络基本结构 超参数超参数是什么常用超参数超参数搜索过程常用超参数调优办法&#xff08;通过问题复杂度和计算资源选择&#xff09; 激活函数介绍为什么要使用激活函数推荐博客 sigmoid激活函数&#xff08;使用…

【教程-时间序列预测】PyTorch 时间序列预测入门

文章目录 from博客: https://zhajiman.github.io/post/pytorch_time_series_tutorial/#%E9%AB%98%E7%BA%A7%E6%96%B9%E6%B3%95%E8%87%AA%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B 数据集产生 窗口 也是难点&#xff01;

工作中,如何有效解决“冲突”?不回避,不退让才是最佳方式

职场里每个人都在争取自己的利益&#xff0c;由于立场的不同&#xff0c;“冲突”不可避免。区别在于有些隐藏在暗处&#xff0c;有些摆在了台面上。 隐藏在“暗处”的冲突&#xff0c;表面上一团和气&#xff0c;实则在暗自较劲&#xff0c;甚至会有下三滥的手段&#xff1b;…

常见API(二)

API 应用程序编程接口&#xff0c;提高编程效率。本次学习了Object类&#xff0c;Objects工具类&#xff0c;包装类&#xff0c;StringBuilder&#xff0c;StringBuffer&#xff0c;和StringJoiner。 目录 1.Object 常见方法&#xff1a; 2.Objects 常见方法&#xff1a; 3…

AcWing开放地址法和拉链法

开放地址法&#xff0c;把h数组全部设置为3f&#xff0c;然后设定null为0x3f3f3f3f&#xff0c;find函数设定返回值t&#xff0c;如果h[t]null,那么x在h中不存在&#xff0c;否则为存在 #include<iostream> #include<cstring> #include<string> #define LEN…

在Vue3中如何为路由Query参数标注类型

前言 最近发布了一款支持IOC容器的Vue3框架&#xff1a;Zova。与以往的OOP或者Class方案不同&#xff0c;Zova在界面交互层面仍然采用Setup语法&#xff0c;仅仅在业务层面引入IOC容器。IOC容器犹如一把钥匙&#xff0c;为我们打开了业务工程化的大门&#xff0c;允许我们探索…

Linux网络编程2

TCP编程 顺序图 socket() 函数 socket()函数用于创建一个新的套接字。它是进行网络编程的第一步&#xff0c;因为所有的网络通信都需要通过套接字来进行。 原型&#xff1a; #include <sys/socket.h> int socket(int domain, int type, int protocol); domain&…

使用Go语言绘制饼图的教程

使用Go语言绘制饼图的教程 在本教程中&#xff0c;我们将学习如何使用Go语言及gg包绘制饼图&#xff0c;并将其保存为PNG格式的图片。饼图是一种常用的数据可视化图表&#xff0c;用于展示数据的比例关系和组成部分。 安装gg包 首先&#xff0c;确保你已经安装了gg包。如果还…

前端的学习-CSS(七)

一&#xff1a;定位(position) 1&#xff1a;为什么使用定位。 有一些固定的盒子&#xff0c;并且压在其他盒子上面&#xff0c;一些网页&#xff0c;会有固定的窗口&#xff0c;这些是标准流和浮动无法解决的&#xff0c;比如下面的例子。旁边的红色边框的效果是不会随着页面的…

汇昌联信数字做拼多多运营怎么入行?

拼多多作为中国领先的电商平台之一&#xff0c;近年来在数字运营领域展现出了强大的生命力和创新能力。汇昌联信数字作为一个潜在的新入行者&#xff0c;如何进入拼多多的运营领域&#xff0c;成为业界关注的焦点。本文旨在探讨汇昌联信数字如何通过有效的策略和方法&#xff0…

hal库回调函数机制

1. 第一行就是标准库函数的 在 nvic那个中断向量表里面的函数 以前写的都是 在中断向量表里面把这个中断处理函数重写 2. 第二行 第三行 的 hal库就是 通过中断向量表里面的这个函数 &#xff0c;再一次调用hal自己的中断回调函数&#xff0c;就是相当于多封装了两层 这个图更…

局部整体(二)利用python绘制维恩图

局部整体&#xff08;二&#xff09;利用python绘制维恩图 维恩图&#xff08; Venn Diagram&#xff09;简介 维恩图显示集与集之间所有可能存在的逻辑关系&#xff0c;每个集通常以一个圆圈表示&#xff0c;每个集都是一组具有共同之处的物件或数据。当多个圆圈&#xff08;…

【Material-UI】File Upload Button 组件详解

文章目录 一、基础实现1. component"label"2. 隐藏的输入元素 二、样式和交互增强1. 自定义按钮样式2. 交互提示 三、支持多文件上传四、无障碍性&#xff08;Accessibility&#xff09;1. 提供 aria-label 或 aria-labelledby2. 支持键盘导航 五、高级用法和集成1. …

leetcode-215. 数组中的第K个最大元素

题目描述 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2,1,5…

STM32-低功耗模式详解

一、概述 低功耗模式&#xff08;Low Power Mode&#xff09;是为了减少电子设备的能耗而设计的操作模式&#xff0c;广泛应用于依赖电池供电的设备中&#xff0c;旨在延长电池寿命或减少能源消耗。在用户需要设备长时间工作或在电量极为有限的情况下非常实用&#xff0c;虽然牺…

如何在IDEA上使用JDBC编程【保姆级教程】

目录 前言 什么是JDBC编程 本质 使用JDBC编程的优势 JDBC流程 如何在IEDA上使用JDBC JDBC编程 1.创建并初始化数据源 2.与数据库服务器建立连接 3.创建PreparedStatement对象编写sql语句 4.执行SQL语句并处理结果集 executeUpdate executeQuery 5.释放资源 前言 在…