Unity跳跳跳障碍

发布时间:2024年01月09日

小人跳跃?

点击空格,人跳障碍 Project-3

  1. 钉钉文档中导入背景包
  2. 给小人加刚体,选中小人检查器中 添加组件 选择Rigidbody添加刚体
  3. 运行后会发现小人掉地面以下去了,应为地面和小人均添加碰撞体Box Collider,注意调节小人的碰撞体方格大小
  4. 项目Assets中添加文件夹Scripts用于存放代码文件
  5. Scripts创建一个PlayerController.cs文件,关联给小人,代码中获得小人的刚体
public Rigidbody rb;//公有的可以在检查窗口访问,私有的只能在代码中访问
rb = GetComponent<Rigidbody>();//在start中
//获取刚体Rigidbody组件
//相比将rb设为public后,在编辑器中直接拖拽更加稳定,防止拖拽的对象出错不稳定,拖拽之后没赋值成功
//检查器中的属性显示两个单词之间有空格是是为了区分,在代码中没空格
  • 为刚体施加力,使小人能跳起?,如果连续按下空格会持续施加力,小人越跳越高,不符合题目只跳一个单位高度。
public float jumpForce = 700f;
if (Input.GetKeyDown(KeyCode.Space))//update中,如果按下空格键,返回true
rb.AddForce(Vector3.up*jumpForce,ForceMode.Impulse);//对刚体施加力
//方向*力,瞬时爆发力
  • 设置一个标识符只有当小人落地时才能再次按空格跳起
private bool isOnGround = true;//一个标识符,true代表在地面上,false代表不在地面上
 private void Update()
 {
     if (Input.GetKeyDown(KeyCode.Space)&&isOnGround)
     {
         rb.AddForce(Vector3.up*jumpForce,ForceMode.Impulse);//方向*力,瞬时爆发力
         isOnGround = false;
     }
 }
  • 检测两个物体碰撞,两个方法
private void OnCollisionEnter(Collision collision)//实现两个物体碰撞 有弹开效果
{
    Debug.Log("OnCollisionEnter");//此函数被调用以此,就在控制台打印一次OnCollisionEnter
    isOnGround = true;//与地面碰撞 此时将标识符赋值true以便可以再次按空格弹跳
}
private void OnTriggerEnter(Collider other)//实现两个物体碰撞 无弹开效果 ,用此方法需要在检查器中两个中的一个对象中的BoxCollider中的触发器勾选上
{//只是监测碰撞上了 不做处理 不由物理引擎做处理 由自己写代码做处理
    Debug.Log("OnTriggerEnter");
}
  • 此题目用?private void OnCollisionEnter(Collision collision),不用private void OnTriggerEnter(Collider other)因为,此方法勾选两个碰撞物体中的某一物体勾选触发器后,小人会直接掉下去,无碰撞效果,用private void OnCollisionEnter(Collision collision)不会掉下去,弹开效果不明显,防止小人掉下去
  • 调节小人下降速度,通过改变g
public float gravityModifer;//在编辑器中可以填数
Physics.gravity*= gravityModifer;//start中,修改重力g

背景和障碍物移动

  • Scripts中创建一个MoveLeftcs文件,用于图片的往左移动,移动一部分图片再回到原来位置,图片的位移使得人物看上去是在动的效果
  • 这种标位置具体数字不推荐,很麻烦
fublic class MoveLeft : MonoBehaviou
{
    public float speed = 15f;
    private void Update()
    {
        transform.Translate(Vector3.left *speed*Time.deltaTime);//图片以某一速度匀速往左动
         if (tarnsformpostion.postion.x<=-10)
        {
           transform.postion=new Vector(47,9.5f,4f);//两张图片连一起整体图片的初始位置Vector(47,9.5f,4f),地面最左端位置是-10
        }
        
    }
}
  • 保存图片对象运动开始位置值进行后续操作
public class MoveLeft : MonoBehaviour
{
    public float speed = 15f;
    private Vector3 startPos;
    private void Start()
    {
        startPos = transform.position;//图片初始位置
    }
    private void Update()
    {
        transform.Translate(Vector3.left *speed*Time.deltaTime);
        if (!gameObject.CompareTag("Obstacle"))//判断对象标签与Obstacle是不是相等
        {
            BoxCollider colider = GetComponent<BoxCollider>();//准备通过BoxCollider获取图片对象的大小 size.x即他的长度
            if (transform.position.x <= startPos.x - colider.size.x / 2)//初始位置(这里的x是位置)往左移动了图片的一半,一到这个位置就执行if里面的语句
            {
                transform.position = startPos;
            }
        }
        
    }
}
  • 选择一个障碍物放入屏幕中,让障碍物移动 ,依然可以将MoveLeft.cs关联给障碍物,分别给障碍物与图片背景对象加上标签,分别为Obstacle和Background,加标签用来区分连个对象,不用名字是因为名字更容易被更改
  • 背景重复移动,首先获取背景图片的刚体(无法直接获取图片对象的大小,只能通过他的刚体中的size属性拿到)

public class MoveLeft : MonoBehaviour
{
    public float speed = 15f;
    private Vector3 startPos;
    private void Start()
    {
        startPos = transform.position;//图片初始位置
    }
    private void Update()
    {
        transform.Translate(Vector3.left *speed*Time.deltaTime);
        if (!gameObject.CompareTag("Obstacle"))//判断对象标签与Obstacle是不是相等
        {//不相等即是背景图片才进行内部代码,到一定位置立刻回到原位置
            BoxCollider colider = GetComponent<BoxCollider>();//准备通过BoxCollider获取图片对象的大小 size.x即他的长度
            if (transform.position.x <= startPos.x - colider.size.x / 2)//初始位置(这里的x是位置)往左移动了图片的一半,一到这个位置就执行if里面的语句
            {
                transform.position = startPos;
            }
        }
        
    }
}

生成多个障碍物(预制体)

  • Assets文件下新建一个名为Prefabs文件夹,存预制体,将界面制作好的障碍物拖拽到此文件夹内生成原始预制体,此时可以把界面的障碍物删除了
  • 在界面项目中创建一个空对象名为Spawn Manager,用于生成多个对象
  • 在Scripts文件夹中新建一个SpawnManager.cs脚本与Spawn Manager关联
public class SpawnManager : MonoBehaviour
{
    public GameObject obstaclePrefab;
    private void Start()
    {
        InvokeRepeating("SpawnObstacle",2f,2f);//定时器,重复发调用,每隔一段时间调用此方法SpawnObstacle,方法,延迟,时间间隔(默认2s)
    }
    private void Update()
    {
    }
    void SpawnObstacle()
    {
        Instantiate(obstaclePrefab);//克隆
    }
}

动画

?复杂动画使用专业软件做好的,导进Unity用动画器进行管理,简单动画可以用动画进行制作?

?鼠标滚轮前后放大缩小,Alt+鼠标左键可移动整体图,每个小方块都是一个动画片段,橙色的为默认动画(一般第一个动画为默认动画,也可以将其他的设为默认动画)

可以找到方块对应的动画片段进行播放,观看动画片段

被点击的指向线变蓝色右侧Conditions内的显示对参数的控制,左侧参数编写时例如Speed_f(速度 浮点型float),根据右侧Speed_f(0.25)可知当左侧Speed_f对应的值在0-0.25之间则是walk_Static等等,依次靠参数调整状态

  • 通过代码修改动画器中的参数值进而使对象处于某种状态
 private Animator animator;//为获取一个动画器
private void Start()
{
    rb = GetComponent<Rigidbody>();//获取刚体Rigidbody组件
    //检查器中的属性显示两个单词之间有空格是是为了区分,在代码中没空格,与拖拽的区别是这种更稳定
    Physics.gravity*= gravityModifer;//打印重力
    animator=GetComponent<Animator>();//为获取一个动画器,<>:泛型,
    //GetComponent("Animator")也可以但不规范
    animator.SetFloat("Speed_f", 0.8f);//修改Speed_f值使其处于某个过程线范围内处于某种状态
}
  • 自己制作动画

Assets下创建一个新的文件夹Animatitons用来存储动画器,在文件夹内创建一个动画器名为Player?

??

选中小人Player在检查器中添加组件Animator,将动画器Player拖拽到控制器处?,控制器下方的Avatar为骨架,必须赋值骨架,否则无法实现动画

双击控制器Player进入如下界面,idle和Run_Static为后拖拽进来的,鼠标选中方块右键建立过度线,左侧参数点击“+”,添加参数(注意类型),Speed为float型,点击过度线右侧检查器中Condition中选择限制参数Speed,中间为大于小于选择(less为小于),最后设置具体数值(例小于0.5)进入idle状态。

退出时间(勾选时,看蓝色时间线,必须等Run_Static执行完再进入idel)未勾选则满足条件直接进入idel

如果参数为Triger(触发器类型),如右下Conditions中的Jump_trig,则无控制条件,直接触发指向的动作?

若想从Run_Static到Running_Jump,则可以参数中添加一个boolea类型Jump(可以是Run_Static到Running_Jump,点击空格按钮小人跳跃时Jump设为true,Running_Jump到Run_Static人与地碰撞函数中Jump设为false)

  • 为小人创建动画?

Assets文件下创建控制器PlayerAnim,将其拖拽给小人的控制器赋值?

双击进入动画器内部,将动画Run、Running_Jump、Death_01拖拽进控制器里,进行连写过渡,Run到Running_Jump(在控制器参数中添加Bool型名为Jump变量,在小人关联的脚本PlayerController中当点击空格需要将Run转换为Running_Jump设Jump为true,当小人跳起再次落地与地碰撞时将设Jump为false),当小人碰到障碍物时会倒下,所以Run和Running_Jump均指向Death_01(参数中添加一个名为Dead类型为Triger,PlayerController中获取与小人碰撞的对象的标签,如果是障碍物标签,则将设置Triger)

public class PlayerController : MonoBehaviour
{
    public Rigidbody rb;//公有的可以在检查窗口访问,私有的只能在代码中访问
    public float jumpForce = 700f;
    public float gravityModifer;
    private bool isOnGround = true;//一个标识符,true代表在地面上,false代表不在地面上
    private Animator animator;//为获取一个动画器
    private void Start()
    {
        rb = GetComponent<Rigidbody>();//获取刚体Rigidbody组件
        //检查器中的属性显示两个单词之间有空格是是为了区分,在代码中没空格,与拖拽的区别是这种更稳定
        Physics.gravity*= gravityModifer;//打印重力
        animator=GetComponent<Animator>();//为获取一个动画器
        animator.SetFloat("Speed_f", 0.8f);//修改Speed_f值使其处于某个过程线范围内处于某种状态
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&isOnGround)
        {
            rb.AddForce(Vector3.up*jumpForce,ForceMode.Impulse);//方向*力,瞬时爆发力
            isOnGround = false;
            animator.SetBool("Jump", true);
        }
    }
    private void OnCollisionEnter(Collision collision)//实现两个物体碰撞 有弹开效果
    {
        //Debug.Log("OnCollisionEnter");//此函数被调用以此,就在控制台打印一次OnCollisionEnter
        if (collision.gameObject.CompareTag("Ground"))//获得碰撞上的物体collision的标签
        {
            isOnGround = true;//与地面碰撞 此时将标识符赋值true以便可以再次按空格弹跳
            animator.SetBool("Jump", false);
        }
        if (collision.gameObject.CompareTag("Obstacle"))
        {
            animator.SetTrigger("Dead");//Triger类型,直接调用即触发
        }
    }

?预制体

  • 在层级中创建一个对象,在Assets下创建一个文件夹名为Prefabs用于存储预制体,将创建的对象拖入Prefabs中,弹出创建原始预制体(相当于一个模子),此时可以创建一个空对象Spawn Manager在层级中,为其编写脚本SpawnManager(脚本名字不要有空格)可以实现不断生成预制体对象(脚本关联给Spawn Manager),将预制体拖拽到层级里此时层级里的Obstacle对象和Prefabs中的预制体Obstacle不是一个东西,如果将层级里的Obstacle再次拖拽到Prefabs里,此时弹出创建原始预制体还是预制体变体,若选择原始预制体则新创建的Obstacle和原来的预制体Obstacle是不一致的,改变原来的新创建的不会变,若选择预制体变体则新创建的Obstacle是原来预制体的变体(类似子类),父类Obstacle(原来的)一变,子类Obstacle也会变

  • ?预制体销毁

1)可以在与预制体关联的脚本中,利用销毁位置进行写代码销毁

2)可以在层级窗口生成多个预制体对象所利用的空对象所关联的脚本中,每次生成对象写代码控制销毁时间

3)可以在与预制体关联的脚本中,利用销毁时间写代码实现

public class MoveLeft : MonoBehaviour
{
    public float speed = 15f;
    public float leftBound = -15f;
    private Vector3 startPos;
    private void Start()
    {
        startPos = transform.position;//图片初始位置
        //Invoke("DestroySelf", 3f);//只调用一次
    }
    private void Update()
    {
        transform.Translate(Vector3.left *speed*Time.deltaTime);
        //if (this.transform.position.x < leftBound)
        //{
        //    Destroy(this.gameObject);
        //}
        if (!gameObject.CompareTag("Obstacle"))//判断对象标签与Obstacle是不是相等
        {
            BoxCollider colider = GetComponent<BoxCollider>();//准备通过BoxCollider获取图片对象的大小 size.x即他的长度
            if (transform.position.x <= startPos.x - colider.size.x / 2)//初始位置(这里的x是位置)往左移动了图片的一半,一到这个位置就执行if里面的语句
            {
                transform.position = startPos;
            }
        }
        
    }
    void DestroySelf()
    {
        Destroy(this.gameObject);
    }
}
public class SpawnManager : MonoBehaviour
{
    public GameObject obstaclePrefab;//预制体
    private void Start()
    {
        InvokeRepeating("SpawnObstacle",2f,2f);//定时器,重复发调用,每隔一段时间调用此方法SpawnObstacle,方法,延迟,时间间隔(默认2s)
    }
    private void Update()
    {
    }
    void SpawnObstacle()
    {
        GameObject obstacle=Instantiate(obstaclePrefab);//克隆
        Destroy(obstacle, 3f);//生成预制体时便直接3秒后销毁
    }
}
  • ?预制体与预制体对象,引用其他脚本中的内容

1)若想在MoveLeft(关联给了预制体和背景图片)中引用PlayerController(关联给小人),在MoveLeft中建立一个PlayerController类,若从项目文件中直接将PlayerController脚本拖拽到界面预制体检查器中对应位置,是拖不进去的,若将预制体拖到层级处形成预制体对象,再将脚本托到检查器对应位置也是拖不进去的,因为PlayerController(关联给小人),所以将层级中的Player(小人对象)托给预制体对象对应的检查器中位置,能拖进去,此时复制成功,在预制体对象关联的MoveLeft脚本中可以使用PlayerController里面的东西。

2)MoveLeft中写代码去整个项目中找对象

private PlayerController playerController;
private void Start()
{
    startPos = transform.position;//图片初始位置
    //Invoke("DestroySelf", 3f);//只调用一次
    GameObject player = GameObject.Find("Player");//去整个场景中找Player对象
    playerController=player.GetComponent<PlayerController>();//在Player对象中找PlayerController
}

3)?MoveLeft中写代码去整个项目中找PlayerController

        playerController = FindAnyObjectByType<PlayerController>();//整个场景中直接找PlayerController
  • 注意:预制体是文件,在运行之前即存在,对象是运行之后new出来的,所以将层级中的Player(小人对象)托给预制体对应的检查器中位置,不能拖进去;此外,文件中的PlayerController和小人关联的PlayerController不是同一个文件,小人关联的PlayerController是在游戏运行后生成的,而文件中的PlayerController一直存在

  • 停止后不在生成障碍物
public class SpawnManager : MonoBehaviour
{
    public GameObject obstaclePrefab;//预制体
    private PlayerController playerController;

    private void Start()
    {
        playerController = FindAnyObjectByType<PlayerController>();//整个场景中直接找PlayerController
        InvokeRepeating("SpawnObstacle",2f,2f);//定时器,重复发调用,每隔一段时间调用此方法SpawnObstacle,方法,延迟,时间间隔(默认2s)

    }
    private void Update()//实时监测
    {
        if (playerController.gameOver)
        {
            CancelInvoke("SpawnObstacle");//一旦游戏结束,取消定时器调用函数
        }
    }
    void SpawnObstacle()
    {
        if (!playerController.gameOver)//但这种情况虽然不会再克隆障碍物了,但依然会此函数依然在不断调用,需取消定时器
        {
            GameObject obstacle = Instantiate(obstaclePrefab);//克隆
            Destroy(obstacle, 3f);//生成预制体时便直接3秒后销毁
        }
    }
}

加特效动画

  • 为小人加奔跑特效,加碰撞爆炸特效
public class PlayerController : MonoBehaviour
{
    public Rigidbody rb;//公有的可以在检查窗口访问,私有的只能在代码中访问
    public float jumpForce = 700f;
    public float gravityModifer;
    private bool isOnGround = true;//一个标识符,true代表在地面上,false代表不在地面上
    private Animator animator;//为获取一个动画器
    public bool gameOver;//true游戏结束 false游戏未结束
    public ParticleSystem explosionParticle;//爆炸 粒子特效类
    private void Start()
    {
        rb = GetComponent<Rigidbody>();//获取刚体Rigidbody组件
        //检查器中的属性显示两个单词之间有空格是是为了区分,在代码中没空格,与拖拽的区别是这种更稳定
        Physics.gravity*= gravityModifer;//打印重力
        animator=GetComponent<Animator>();//为获取一个动画器
        //animator.SetFloat("Speed_f", 0.8f);//修改Speed_f值使其处于某个过程线范围内处于某种状态
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&isOnGround)
        {
            rb.AddForce(Vector3.up*jumpForce,ForceMode.Impulse);//方向*力,瞬时爆发力
            isOnGround = false;
            animator.SetBool("Jump", true);
        }
    }
    private void OnCollisionEnter(Collision collision)//实现两个物体碰撞 有弹开效果
    {
        //Debug.Log("OnCollisionEnter");//此函数被调用以此,就在控制台打印一次OnCollisionEnter
        if (collision.gameObject.CompareTag("Ground"))//获得碰撞上的物体collision的标签
        {
            isOnGround = true;//与地面碰撞 此时将标识符赋值true以便可以再次按空格弹跳
            animator.SetBool("Jump", false);
        }
        if (collision.gameObject.CompareTag("Obstacle"))
        {
            animator.SetTrigger("Dead");//Triger类型,直接调用即触发
            gameOver = true;
            ParticleSystem explosion = Instantiate(explosionParticle);//碰撞时产生爆炸特效,实例化对象
            explosion.Play();//爆炸动画播放
        }
    }
  • 不用加播放代码,直接实例化爆炸特效即可运行后自动播放,需要勾选检查器中唤醒时播放

  • 碰撞时游戏停止,动画结束?

在动画结束编写过程中,PlayerController文件中引用两个动画进行控制,奔跑跟随动画应该拖拽Player中的奔跑动画,爆炸动画拖拽文件中的动画

添加背景音乐

  • 将背景音乐文件拖拽到场景中的任意对象里都可以播放,但习惯托给主相机或者创建一个空对象拖拽进去
  • 添加跳等音乐

需要再小人对象里添加组件

注意?

  • 控制台打印
int a;
print(a);//控制台打印这两种都可以
Debug.Log(a);
//当变量a不赋值时,int型打印0,float型打印0,bool型打印false(看是否界面中检查器变量勾选),string型打印空字符串,GameObject型打印null
  • 如果代码运行报错

去控制台看,有红色叹号,点击其中一个,看下方报错详细信息,下方蓝色部分点击跳转到报错位置?

源码

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

public class SpawnManager : MonoBehaviour
{
    public GameObject obstaclePrefab;//预制体
    private PlayerController playerController;

    private void Start()
    {
        playerController = FindAnyObjectByType<PlayerController>();//整个场景中直接找PlayerController
        InvokeRepeating("SpawnObstacle",2f,2f);//定时器,重复发调用,每隔一段时间调用此方法SpawnObstacle,方法,延迟,时间间隔(默认2s)

    }
    private void Update()//实时监测
    {
        if (playerController.gameOver)
        {
            CancelInvoke("SpawnObstacle");//一旦游戏结束,取消定时器调用函数
        }
    }
    void SpawnObstacle()
    {
        if (!playerController.gameOver)//但这种情况虽然不会再克隆障碍物了,但依然会此函数依然在不断调用,需取消定时器
        {
            GameObject obstacle = Instantiate(obstaclePrefab);//克隆
            Destroy(obstacle, 3f);//生成预制体时便直接3秒后销毁
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveLeft : MonoBehaviour
{
    public float speed = 15f;
    public float leftBound = -15f;
    private Vector3 startPos;
    // public PlayerController playerController;
    private PlayerController playerController;
    private void Start()
    {
        startPos = transform.position;//图片初始位置
        //Invoke("DestroySelf", 3f);//只调用一次
        //GameObject player = GameObject.Find("Player");//去整个场景中找Player对象
        //playerController=player.GetComponent<PlayerController>();//在Player对象中找PlayerController
        playerController = FindAnyObjectByType<PlayerController>();//整个场景中直接找PlayerController
    }
    private void Update()
    {
        if(playerController.gameOver!=true)//或if(!playerController.gameOver)
        {
            transform.Translate(Vector3.left * speed * Time.deltaTime);
        }
        //if (this.transform.position.x < leftBound)
        //{
        //    Destroy(this.gameObject);
        //}
        if (!gameObject.CompareTag("Obstacle"))//判断对象标签与Obstacle是不是相等
        {
            BoxCollider colider = GetComponent<BoxCollider>();//准备通过BoxCollider获取图片对象的大小 size.x即他的长度
            if (transform.position.x <= startPos.x - colider.size.x / 2)//初始位置(这里的x是位置)往左移动了图片的一半,一到这个位置就执行if里面的语句
            {
                transform.position = startPos;
            }
        }
        
    }
    void DestroySelf()
    {
        Destroy(this.gameObject);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public Rigidbody rb;//公有的可以在检查窗口访问,私有的只能在代码中访问
    public float jumpForce = 700f;
    public float gravityModifer;
    private bool isOnGround = true;//一个标识符,true代表在地面上,false代表不在地面上
    private Animator animator;//为获取一个动画器
    public bool gameOver;//true游戏结束 false游戏未结束
    public ParticleSystem explosionParticle;//爆炸 粒子特效类
    public ParticleSystem dirtyParticle;//奔跑跟随粒子特效

    private AudioSource audioSource;//为获取音乐组件
    public AudioClip jumpClip;//跳音乐,从文件拖入
    public AudioClip deadClip;
    private void Start()
    {
        rb = GetComponent<Rigidbody>();//获取刚体Rigidbody组件
        //检查器中的属性显示两个单词之间有空格是是为了区分,在代码中没空格,与拖拽的区别是这种更稳定
        Physics.gravity*= gravityModifer;//打印重力
        animator=GetComponent<Animator>();//为获取一个动画器
        //animator.SetFloat("Speed_f", 0.8f);//修改Speed_f值使其处于某个过程线范围内处于某种状态
 
        audioSource = GetComponent<AudioSource>();
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)&&isOnGround&&!gameOver)
        {
            rb.AddForce(Vector3.up*jumpForce,ForceMode.Impulse);//方向*力,瞬时爆发力
            isOnGround = false;
            animator.SetBool("Jump", true);
            audioSource.PlayOneShot(jumpClip);//按下空格跳时才会有跳音乐响起
        }
    }
    private void OnCollisionEnter(Collision collision)//实现两个物体碰撞 有弹开效果
    {
        //Debug.Log("OnCollisionEnter");//此函数被调用以此,就在控制台打印一次OnCollisionEnter
        if (collision.gameObject.CompareTag("Ground"))//获得碰撞上的物体collision的标签
        {
            isOnGround = true;//与地面碰撞 此时将标识符赋值true以便可以再次按空格弹跳
            animator.SetBool("Jump", false);
        }
        if (collision.gameObject.CompareTag("Obstacle"))
        {
            animator.SetTrigger("Dead");//Triger类型,直接调用即触发
            gameOver = true;
            ParticleSystem explosion = Instantiate(explosionParticle);//碰撞时产生爆炸特效,实例化对象
                                                                      // explosion.Play();//爆炸动画播放
            dirtyParticle.Stop();
            audioSource.PlayOneShot(deadClip);//碰撞时才会有碰撞音乐响起

        }
    }
    //private void OnTriggerEnter(Collider other)//实现两个物体碰撞 无弹开效果 ,用此方法需要在检查器中两个中的一个对象中的BoxCollider中的触发器勾选上
    //{//只是监测碰撞上了 不做处理 不由物理引擎做处理 由自己写代码做处理
    //    Debug.Log("OnTriggerEnter");
    //}
}

?

?

?

文章来源:https://blog.csdn.net/skx13613650153/article/details/135439289
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。