Unity入门理论+实践篇之Luna

创建世界的主角

父子物体

首先创建一个cube物体

可以观察到其在2D视角下的坐标为(0,0)

此时将cube物体拖拽到ldle_0下,如图所示,并将其坐标值改为(2,2)

 此时再将ldle_0物体的坐标改为(1,1)并观察ldle_0和cube的坐标

此时可以观察到,ldle_0和cube整体位置发生改动,但cube的坐标却没有变,那是因为,cube作为ldle_0的子物体,其坐标值是相对于ldle_0来说,即以ldle_0为原点

此时再将cube拖拽出ldle_0 

此时可以看到cube的坐标值变为(3,3),那是因为cube不再作为ldle_0的子物体,其坐标轴以世界坐标轴为准

 

 1. 现在使物体Luna进行初步的移动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){Vector2 position = transform.position;position.x = position.x + 0.1f;transform.position = position;}
}

Unity中输入系统与C#浮点型变量(Input)

输入系统即监听用户输入的指令,如下图所示为Input所有输入监听的对象

 现在获取水平方向的输入监听

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 0.1f;transform.position = position;}
}

由于Input.GetAxis返回的类型为float(浮点数类型)所以也要创建相应类型的变量去接收它的值,现在开始监听水平方向的输入指令,为了监听输入指令,于是就用了Debug.Log()函数(用于在控制面板上输出)

Input.GetAxis(“Horizontal”)输入范围为-1~1

现在要先通过输入来操纵物体Luna的移动,只需下面代码就可以实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 0.1f * horizontal;position.y = position.y + 0.1f * vertical;transform.position = position;}
}

但是速度过快,所以们需要加入Time.daltaTime使物体Luna以每秒0.1米的速度去移动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 0.1f * horizontal * Time.deltaTime;position.y = position.y + 0.1f * vertical * Time.deltaTime;transform.position = position;}
}

其中 horizonal * Time.daltaTime为1m/s

渲染顺序与层级设置

 如上图所示,将物体BattleBackground02拖入场景时,Luna被覆盖住了,这是因为在2D视角下虽然所有物体的(x,y)为(0,0),但在unity3D渲染中还存在深度这一概念,即在3D视角下,摄像机的z轴值为-10,但BattleBackground02的z轴值为-1,除了这两个物体以外的其他物体的z轴值为0,在视觉上来看,BattleBackground02在其他物体前面,所以后被渲染,这样才出现了Luna被覆盖的情况,为了在2D视角下解决这一问题,方法一是将BattleBackground02的z轴设置为0,但调整深度(z轴)有违于2D游戏的设置理念,所以此时就引出了下面概念,层级

层级(Layer)

可以在2D游戏物体中的SpriteRenderer和地形TilemapRenderer组件中的Order in layer属性中去设置层级的大小,值越大,越后渲染,值越小,越先渲染,值大的游戏物体会覆盖值小的游戏物体。

可以在如上图看到,SpriteRenderer中的Order in layer值都为0,所以渲染顺序是随机,所以在不同电脑下所看到的实际展现效果不同

在实际制作中,层级总会随着人物的变动而变换,如果一个个去调会很麻烦,因此,就需要进行相关的设置如下图

其中default代表根据深度(z轴)来进行渲染 先将其改为Custom Axis(自定义)并将y轴的权重设置为1,z轴为0

中心点与轴心点

如上图所示,Luna已经在马车的轮子上面,但是Luna并没有被覆盖,而是要再往上平移一点才行,因此就要进行细节的设计

如图所示,从视觉上来看物体Luna此时应该被覆盖,但是却没有,因为此时的渲染层是按物体的中心点来渲染的

如图所示House的y轴比Luna高,所以未被覆盖,所以此时就要引出轴心点概念

轴心点(Pivot):

轴心点是可以自定义的特殊点,充当精灵的“锚点”精灵以此为支点进行旋转,坐标点位置则是指轴
心点的位置。

现在只需将horse的轴心点改为如下所示

 Luna的层级改为如下所示

就可以实现覆盖效果

注意:

虽然修改了轴心点,但一定要将Order in layer数值改为相同大小,不然虽然改变了轴心位置,但渲染顺序不一致还是覆盖不了(Luna会被后渲染)

Unity中的预制体(Prefab)

预制体(Prefab)是Unity中的一种特殊资源。预制体就是一个或者一系列组件的集合体,可以使用预制体实例化克降体,后续可对克隆体属性进行统一修改。

创建预制体

预制体作为母体,他做的所有更改都会同步到其克隆体(子体)中

 断开与预制体(母体)的连接,使其成为单独个体

 制定世界的因果定律

Unity中的物理系统

刚体组件和碰撞器组件

刚体组件(Rigidbody):

使游戏物体能获得重力,接受外界的受力和扭力功能的组件,可以通过脚本或是物理引擎为游戏对象添加刚体组件。

现在为物体Luna添加一个刚体组件SS

如上图所示Rigidbody中的gravity scale属性值为1,在实际运行中会使Luna沿着y轴一直掉出地图,为了改变此状态只需将gravity scale属性值改为0即可

碰撞器组件

为了使游戏效果更真实,即当Luna碰到NPC时会被挡住过不去,因此就可以采用到碰撞器,现在为Luna和NPC装上碰撞器组件

但此时发现在Luna碰到NPC时,自身会发生沿着z轴的旋转,为了解决这一问题就需要用到下列操作固定物体在z轴上的角度

发生碰撞检测的条件

双方必须都有碰撞器,且一方有刚体,最好是运动的一方。因为Unity为了性能的优化,刚体长时间不发生碰撞检测会休眠。

解决震动问题

在实际运行中,可以发现,当两个碰撞器发生碰撞时,Luna会出现震动效果,这是因为在使用Rigid body2D和Boxcollider 2D这两个组件时,物体会根据unity中已经设置好的固有逻辑(脚本)去对物体进行移动, 而在实际中物体会先根据用户输入的指令进行移动,然后在由系统脚本进行碰撞检测,若是发生碰撞,系统逻辑会将物体移出,这将造成了用户输入指令和体统逻辑判定的一个滞后问题,从而发生震动

为了解决这个问题,我们可以修改代码,使其直接控制游戏物体的刚体,使物体进行移动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + 3 * horizontal * Time.deltaTime;position.y = position.y + 3 * vertical * Time.deltaTime;//transform.position = position;rigidbody.MovePosition(position);}
}

实体化游戏世界地形并可与游戏人物交互

如下图可以发现,Luna是可以横穿整个地图,但在实际开发中Luna碰到山体是是应该停止的,所以就需要用到碰撞器

现在在Maps下创建一个空物体并添加多面体碰撞器(Polygon Collider 2D),并及逆行相应的编辑

 创世的逻辑思维

访问级别与访问修饰符

public :公开的、公共的;

private: 私有的、只能在当前类的内部访问;

protected:受保护的、只能在当前类的内部以及子类中访问;

internal: 只能在当前项目中访问,在项目外无法访问。当时在本项目中 public 与 internal的访问权限是一样的。

protected internal:protected + internal  只能在本项目的该类及该类的子类中访问。

能够修饰类的访问修饰符只有两个:public 、internal;

在修饰类时如果没有修饰符,则默认为 internal。

class Program
{
    //默认为 internal,仅能在本项目中进行访问
}
2、internal vs protected

在同一项目中,internal的访问权限比protected的访问权限要大;但是当不在同一个项目中时,protected可以在项目外的子类中被访问,而internal只能在它的本项目中进行访问。

3、访问权限不一致

子类的访问权限不能高于父类的访问权限,不然会暴露父类的成员。

该文引用 https://blog.csdn.net/qq_61709335/article/details/129767609

编写生命值

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;public float movespeed = 0;public int maxHealth = 5;//最大生命值private int currentHealth;//Luna的当前最大生命值// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();currentHealth = maxHealth;}// Update is called once per framevoid Update(){float horizontal = Input.GetAxis("Horizontal");float vertical = Input.GetAxis("Vertical");Debug.Log(horizontal);Vector2 position = transform.position;position.x = position.x + movespeed * horizontal * Time.deltaTime;position.y = position.y + movespeed * vertical * Time.deltaTime;/*transform.position = position;*/rigidbody.MovePosition(position);}public void ChangeHealth(int amount){currentHealth = Mathf.Clamp(currentHealth + amount,0,maxHealth);Debug.Log(currentHealth + "/" + maxHealth);}
}

函数、函数签名、返回值与重载

1、构成重载的条件:参数类型不同或者参数个数不同(不严谨的),与返回值无关。

2、函数重载:同名的两个函数的签名(参数的类型、顺序、个数)不能完全一致,否则就会异常。当两个函数同名但签名不一样时,两个都可以正常使用,即函数重载。函数重载是正确的也是常用的。和参数名无关。函数名拼写一样只是大小写不一样时,属于函数不同名,C#大小写敏感。

3、两个函数是否可以重名,跟函数的返回值类型无关。只要函数的签名,即参数的类型、顺序、个数不一样就行。只要参数的类型、顺序、个数不一致才能函数重名,函数返回值类型一致与否无关。

变量赋值的顺序

根剧代码的逐行编译规则,我们可知,以上面所示代码为例,对于maxhealth这一变量而言,若在start函数里对其进行复制,再在监视面板里对其赋值,其最终的覆盖顺序为1. public int maxHealth = 5 2. start 3. 监视面板,最终呈现出的结果为监视面板里所设置的值

触发器与发生触发检测的条件

  1. 至少一方需要有刚体(Rigidbody)或Kinematic刚体:如果两个物体都要参与触发事件,通常至少其中一个物体需要附有Rigidbody或Rigidbody2D(对于2D游戏)组件。如果使用的是静态物体(如地形),作为触发的一方则不必附加刚体。但是,如果双方都是静止的Kinematic刚体,则无法触发事件。

  2. 设置Is Trigger属性:在Inspector面板中,需要将至少一个碰撞器的“Is Trigger”选项勾选上,将其标记为触发器。这样,当这个标记为触发器的碰撞器与其他碰撞器重叠时,会触发特定的事件,而不是进行物理碰撞。

  3. 编写事件处理代码:为了响应触发器事件,你需要在脚本中编写相应的事件处理函数。对于3D对象,常用的方法是OnTriggerEnter, OnTriggerStay, 和 OnTriggerExit;对于2D对象,则是OnTriggerEnter2D, OnTriggerStay2D, 和 OnTriggerExit2D。这些方法会在进入触发区域、停留于触发区域、以及离开触发区域时被调用。

  4. 添加 collider 并确保重叠:确保参与触发的物体都有合适的碰撞器(Collider)或触发器(Collider)组件,并且它们的形状和尺寸能够正确覆盖你希望触发事件发生的区域。

制作血瓶并实现回血功能

创建脚本之间的联系

为了实现Luna实现回血功能的效果,现在创建一个物体Potion并插入相应的C#脚本,在其中装上触发检测

 现在通过脚本观测触发器碰到Luna的那个组件

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){Debug.Log(collision);}
}

现在实现只要碰到血瓶血条+1的效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*/LunaController luna = collision.GetComponent<LunaController>();luna.ChangeHealth(1);}
}

现在实现Luna碰撞血瓶后,血瓶消失

属性的声明定义与使用

 在前面的脚本编写过程中,在脚本potion中是可以直接访问脚本LunaController的各个属性值(因为是public),但是为了防止人为开挂行为的出现(比如说把maxHealth的值调到无上限),所以就需要把各个属性进行封装

封装(Encapsulation)

封装是将数据和行为包装在一起形成类的过程,将数据隐藏在类的内部,只能通过类的公共方法来访问和修改。C# 中,我们可以使用访问修饰符来实现封装,如 private、protected、internal 和 public 等。

私有成员只能在类的内部访问,受保护的成员可以在派生类中访问,内部成员可以在同一个程序集中访问,公共成员可以被所有代码访问。
通过封装,可以避免数据被误用或修改,增强了程序的安全性和可维护性。

示例如下:

调用封装的

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*/LunaController luna = collision.GetComponent<LunaController>();if(luna.Health < luna.MaxHealth){luna.ChangeHealth(1);}Destroy(gameObject);}
}

赋予任务个人的意识与行为

添加动画制作组件并创建动画控制器

 动画控制器(Animator Controller)

根据当前游戏对象的状态控制播放对应的游戏动画。也可以叫做动画状态机。

动画(Animation)

Unity中的动画资源,可以由动画控制器,代码去进行播放,也是一种组件。

现在为Luna赋予个人意识和动作

创建一个Animator组件

为了更好地管理动画制作组件,要创建一个Animators文件保存Animator Controller,以及为了更好地管理每个人物的动作,创建一个Animations文件,并在Animators文件下创建Animator Controller文件,文件名Luna

注:此时Luna有预制体,应对其预制体进行添加

动画状态机与状态机的过渡条件

现在进行动画的制作

打开动画制作窗口

打开动画状态机面板

将已有的图片拖入动画制作窗口,并设置保存位置,如此便可一创作出动画,并为每个动作加上关联

通过如上图所示,创建完人物动作以后,众所周知,在实际中每个人物的状态切换,都会有一个准备动作,如走——跑,但在2D游戏制作中是不需要这样的,我们需要的是跟控制机器一样地窗台切换,所以就需要调整过渡条件

通过上图可知,Unity3D对我们物体动画动作之间的切换进行了融合,现在我们需要去掉融合

但是此时发现我们的动画并没有播放完,只需要将Exit time值改为1即可

混合书地使用方式和意义

在游戏设计中,一个人物走路地方向有八个方位,这样创建动作之间地过渡条件会变得很多,形成蛛网,为了建设动画设计窗口的复杂程度,就引出了混合树的概念

混合树(Blend Tree)

用于允许通过按不同程度组合所有动画的各个部分来平滑混合多个动画各个运动参与形成最终效果的量使用混合参数进行控制,该参数只是与动画器控制器关联的数值动画参数之一。要使混合运动有意义,混合的运动必须具有相似性质和时间。混合树是动画器控制器中的特殊状态类型,

下图是已经别写好的部分以及相关图像运动

通过使人物运动

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;public float movespeed = 30;private int maxHealth = 5;//最大生命值private int currentHealth;//Luna的当前最大生命值private Vector2 lookDirection;public int Health { get { return currentHealth; } }//返回currentHealthpublic int MaxHealth { get { return maxHealth; } }private Animator animator;// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();currentHealth = maxHealth;animator = GetComponentInChildren<Animator>();}// Update is called once per framevoid Update(){//监听玩家输入float horizontal = Input.GetAxis("Horizontal");//水平float vertical = Input.GetAxis("Vertical");//垂直Vector2 move = new Vector2(horizontal, vertical);animator.SetFloat("MoveValues", 0);/*Debug.Log(horizontal);*///当前玩家输入某个轴向不为0if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0)){//判断参数近似相等lookDirection.Set(move.x, move.y);lookDirection.Normalize();//单位化animator.SetFloat("MoveValues", 1);}//动画控制animator.SetFloat("LookX", lookDirection.x);animator.SetFloat("LookY", lookDirection.y);//物体控制 Vector2 position = transform.position;position.x = position.x + movespeed * horizontal * Time.deltaTime;position.y = position.y + movespeed * vertical * Time.deltaTime;/*transform.position = position;*/rigidbody.MovePosition(position);}public void ChangeHealth(int amount){currentHealth = Mathf.Clamp(currentHealth + amount,0,maxHealth);Debug.Log(currentHealth + "/" + maxHealth);}
}

从实际上来看,虽然实现了跑步效果,但发现人物移动速度有时候快有时候慢,所以就需要把物理相关的函数放在FixedUpdate()里   

神之眼

Unity中的摄像机

导入包使摄像机跟随人物移动

下载好以后创建如下图所示相机类型

下载完后,Main Camera中会被自动插入相关组件如下图所示

两种不同的摄象模式(perspective,orthographic)

perspective(一般在3D游戏中使用)

orthographic(一般在2D游戏中使用)

为了使相机跟随Luna进行移动,只需如下步骤即可

但是此时发现当Luna快走到边界时,会出现如下情况

 

为了解决这种情况,需要给摄像机设置一个边界值

如下图所示,为MapW设置一个碰撞器,但一定要将其改为触发器,否则,人物会被弹出地图之外

现在就可以解决上面问题

使用代码生成游戏物体并销毁

实现在Luna吃掉物体后出现星光效果

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{public GameObject effectGo;private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*/LunaController luna = collision.GetComponent<LunaController>();if(luna.Health < luna.MaxHealth){luna.ChangeHealth(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);}}
}

但是现在发现,在Luna吃掉血瓶后出现的星光效果没有消失,现在要使其在播放完一定时间后消失

创建一个effect脚本使特效消失

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class effect : MonoBehaviour
{public float destoryTime;//设置销毁时间// Start is called before the first frame updatevoid Start(){Destroy(gameObject, destoryTime);}// Update is called once per framevoid Update(){}
}

攀爬系统的实现

在动画状态机面板创建Boolean类型的变量Climb,并未动画Climb赋值,如下图所示

因为walk与idle,run动画衔接没有间隔,所以Climb不需要连接run,idle动画,只需连接walk即可

在如上图所示的藤蔓处创建攀爬效果再创建一个碰撞体并将其设置为触发器即可,如下图所示

现在就需要编写代码使Luna在此位置具有攀爬动作

在LunaContriller脚本下编写Climb函数对动画状态机窗口的Climb进行赋值

 public void Climb(bool start){animator.SetBool("Climb", start);}

现在在Climb_area脚本下编写代码进行赋值

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Climb_area : MonoBehaviour
{private void OnTriggerEnter2D(Collider2D collision){/* if(collision.name == "Luna"){}if(collision.tag == "Luna"){}*/if (collision.CompareTag("Luna")){collision.GetComponent<LunaController>().Climb(true);}}
}

但此时发现Luna在进行攀爬动作时具有延迟动作,所以需要修改状态机窗口

神明与世界交互的接口

现在对用户界面的ui进行设计,在进行UI设计时,画布Canvas是必要创建的物体

先创建一个Luna的头像

制作Luna的血条和蓝条,创建Panel_Main文件存放头像,并且将画布属性调成屏幕属性调成屏幕自适应模式(Scale With Screen Size)

在做外如上图所示操作以后,需要进行一个血条填充操作,在血条位置上插入image

从上图可知道血条框是一个不规则图形,所有为了达到预想的呈现效果,进行如下图所示操作,在空物体中创建一个遮罩组件(mask)和image组件

现在需要实现血条的填充效果,只需要改变其缩放就行,但其轴心点需要放在最左侧

同理创造蓝条

给游戏世界带来战争

首先运用单例设计模式将游戏脚本进行管理

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Potion : MonoBehaviour
{public GameObject effectGo;private void OnTriggerEnter2D(Collider2D collision){/* Debug.Log(collision);*//*LunaController luna = collision.GetComponent<LunaController>();if(luna.Health < luna.MaxHealth){luna.ChangeHealth(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);}*/if (Gamemanager.Instance.Health < Gamemanager.Instance.MaxHealth){Gamemanager.Instance.ChangeHealth(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LunaController : MonoBehaviour
{private Rigidbody2D rigidbody;public float movespeed = 1;private Vector2 lookDirection;private Vector2 move;private Animator animator;private float moveScale; //移动状态,停,走,跑// Start is called before the first frame updatevoid Start(){rigidbody = GetComponent<Rigidbody2D>();animator = GetComponentInChildren<Animator>();}// Update is called once per framevoid Update(){//监听玩家输入float horizontal = Input.GetAxis("Horizontal");//水平float vertical = Input.GetAxis("Vertical");//垂直move = new Vector2(horizontal, vertical);animator.SetFloat("MoveValues", 0);/*Debug.Log(horizontal);*///当前玩家输入某个轴向不为0if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0)){//判断参数近似相等lookDirection.Set(move.x, move.y);lookDirection.Normalize();//单位化/*animator.SetFloat("MoveValues", 1);*/}//动画控制moveScale = move.magnitude;if(move.magnitude>0){if(Input.GetKey(KeyCode.LeftShift)){moveScale = 1;}else{moveScale = 2;}}animator.SetFloat("MoveValues", moveScale);animator.SetFloat("LookX", lookDirection.x);animator.SetFloat("LookY", lookDirection.y);}private void FixedUpdate(){//物体控制 Vector2 position = transform.position;/*position.x = position.x + movespeed * horizontal * Time.deltaTime;position.y = position.y + movespeed * vertical * Time.deltaTime;*//*transform.position = position;*/position = position + movespeed * move * Time.fixedDeltaTime;rigidbody.MovePosition(position);}public void Climb(bool start){animator.SetBool("Climb", start);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Gamemanager : MonoBehaviour
{public static Gamemanager Instance;private int maxHealth = 5;//最大生命值private int currentHealth;//Luna的当前最大生命值public int Health { get { return currentHealth; } }//返回currentHealthpublic int MaxHealth { get { return maxHealth; } }public void Awake(){Instance = this;currentHealth = 4;}public void ChangeHealth(int amount){currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);Debug.Log(currentHealth + "/" + maxHealth);}
}

现在将战斗场景拖拽到摄像机前(注意z轴)

现在将战斗状态下的Luna和master拖拽到战斗场景,并调整Flip使怪物正对着Luna(沿x轴翻转180度)

现在进行跳跃系统的实现

在动画状态机窗口创建Jump,其类型为Boolean,在进行如下图所示设置

创建Jump_area管理跳跃区域

并在其中文件安装box collider组件,将其调到相应位置,并设置跳跃点A,B

如图所示,下面场景的都被碰撞器组件所包围,Luna物体具有刚体组件,在进行跳跃时会发生碰撞检测,为了避免这种情况,就可以使Luna的Rigidbody组件暂时休眠(simulated属性打勾不休眠,不打勾休眠),这样就不会发生碰撞检测

如图所示

创建Jump_area脚本控制跳跃

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class Jump_area : MonoBehaviour
{public Transform jumpA;public Transform jumpB;// Start is called before the first frame updateprivate void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("Luna")){LunaController luna = collision.transform.GetComponent<LunaController>();luna.Jump(true);//float disA = Vector3.Distance(luna.transform.position, jumpA.position);float disB = Vector3.Distance(luna.transform.position, jumpB.position);Transform targetTrans;/*if (disA > disB){targetTrans = jumpA;}else{targetTrans = jumpB;}*/targetTrans = disA > disB ? jumpA : jumpB;luna.transform.DOMove(targetTrans.position, 0.5f).OnComplete(() => endJump(luna));}}public void endJump(LunaController luna){luna.Jump(false);}
}
现在需要使跳跃效果更加真实

在此场景下,Luna组件由父物体和子物体一起控制,但在进行碰撞检测时,脚本控制父物体和子物体移动会显得矛盾,因此只需要做到父物体在移动(子物体会跟随着一起移动),为了使运动效果更加逼真,只需调整子物体在有、轴上的位移即可,如下面所示

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class Jump_area : MonoBehaviour
{public Transform jumpA;public Transform jumpB;// Start is called before the first frame updateprivate void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("Luna")){LunaController luna = collision.transform.GetComponent<LunaController>();luna.Jump(true);//float disA = Vector3.Distance(luna.transform.position, jumpA.position);float disB = Vector3.Distance(luna.transform.position, jumpB.position);Transform targetTrans;/*if (disA > disB){targetTrans = jumpA;}else{targetTrans = jumpB;}*/targetTrans = disA > disB ? jumpA : jumpB;//在移动完后执行endJump语句luna.transform.DOMove(targetTrans.position, 0.5f).OnComplete(() => endJump(luna));//获取子物体Transform lunaLocalTrans = luna.transform.GetChild(0);//表示第0个元素Sequence sequence = DOTween.Sequence();//表示动画的演示顺序sequence.Append(lunaLocalTrans.DOLocalMoveY(1.5f, 0.25f));sequence.Append(lunaLocalTrans.DOLocalMoveY(0.61f, 0.25f));sequence.Play();}}public void endJump(LunaController luna){luna.Jump(false);}
}

为了使跳跃的速度由慢变快再变慢,只需如下图所示

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class Jump_area : MonoBehaviour
{public Transform jumpA;public Transform jumpB;// Start is called before the first frame updateprivate void OnTriggerEnter2D(Collider2D collision){if (collision.CompareTag("Luna")){LunaController luna = collision.transform.GetComponent<LunaController>();luna.Jump(true);//float disA = Vector3.Distance(luna.transform.position, jumpA.position);float disB = Vector3.Distance(luna.transform.position, jumpB.position);Transform targetTrans;/*if (disA > disB){targetTrans = jumpA;}else{targetTrans = jumpB;}*/targetTrans = disA > disB ? jumpA : jumpB;//在移动完后执行endJump语句,表示速度以线性的方式呈现luna.transform.DOMove(targetTrans.position, 0.5f).SetEase(Ease.Linear).OnComplete(() => endJump(luna));//获取子物体Transform lunaLocalTrans = luna.transform.GetChild(0);//表示第0个元素Sequence sequence = DOTween.Sequence();//表示动画的演示顺序sequence.Append(lunaLocalTrans.DOLocalMoveY(1.5f, 0.25f).SetEase(Ease.InOutSine));//表示速度以正弦函数的方式呈现sequence.Append(lunaLocalTrans.DOLocalMoveY(0.61f, 0.25f).SetEase(Ease.InOutSine));sequence.Play();}}public void endJump(LunaController luna){luna.Jump(false);}
}

敌人脚本的成员变量与组件的获取

现在将怪物拖拽到场景中,并创建EnemyController脚本控制其运动(巡逻)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EnemyController : MonoBehaviour
{public bool vertical;public float speed = 5;private Rigidbody2D rigidbody2d;//控制方向private int direction = 1;//控制方向改变时间间隔public float changeTime = 5;//计时器public float Timer;//动画控制器private Animator animator;// Start is called before the first frame updatevoid Start(){rigidbody2d = GetComponent<Rigidbody2D>();Timer = changeTime;animator = GetComponent<Animator>();}// Update is called once per framevoid Update(){Timer -= Time.fixedDeltaTime;if (Timer < 0){Timer = 5;direction = -1 * direction;}}private void FixedUpdate(){Vector3 pos = rigidbody2d.position;if (vertical)//垂直方向移动{animator.SetFloat("LookY", direction * 1);animator.SetFloat("LookX", 0);pos.y = pos.y + speed * direction * Time.fixedDeltaTime;}else//水平轴向移动{animator.SetFloat("LookY", 0);animator.SetFloat("LookX", direction * 1);pos.x = pos.x + speed * direction * Time.fixedDeltaTime;}rigidbody2d.MovePosition(pos);}
}

触发战斗与战斗动画控制器

创建如下图所示,战斗场景

现在创建BattleController脚本来控制战斗脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BattleController : MonoBehaviour
{public Animator LunaBattleAnimator;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}public void LunaAttack(){StartCoroutine(PerformAttackLogic());}IEnumerator PerformAttackLogic(){UI_Manager.Instance.ShowOrHideBattlePanel(false);LunaBattleAnimator.SetBool("MoveValues", true);LunaBattleAnimator.SetFloat("MoveValues", -1);yield return null;}
}

再对UImanager创建相关函数控制其的显现(ShowOrHideBattlePanel)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class UI_Manager : MonoBehaviour
{public static UI_Manager Instance;public Image hpmask_image;public Image mpmask_image;public float originalSize;//血条原始宽度public GameObject BattlePanelGo;void Awake(){Instance = this;originalSize = hpmask_image.rectTransform.rect.width;SetHPValue(0.5f);}// Update is called once per framepublic void SetHPValue(float fillPercent){hpmask_image.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, fillPercent * originalSize);}public void SetMPValue(float fillPercent){mpmask_image.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, fillPercent * originalSize);}public void ShowOrHideBattlePanel(bool show){BattlePanelGo.SetActive(show);}
}

对Button_Attack按钮进行赋值

以下是效果展现

但我们很快发现Luna并没有返回,现在需要进行如下设置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;public class BattleController : MonoBehaviour
{public Animator LunaBattleAnimator;public Transform LunaTrans;//获取Luna_Battle和Master的Transform组件public Transform MonsterTrans;private Vector3 monsterInitPos;//获取Luna_Battle、怪物初始位置private Vector3 LunaInitPos;public SpriteRenderer MonsterSr;//获取怪物的SpriteRenderer组件// Start is called before the first frame updatevoid Start(){monsterInitPos = MonsterTrans.localPosition;//赋值为相对于父物体的位置LunaInitPos = LunaTrans.localPosition;}// Update is called once per framevoid Update(){}public void LunaAttack(){StartCoroutine(PerformAttackLogic());}IEnumerator PerformAttackLogic(){UI_Manager.Instance.ShowOrHideBattlePanel(false);LunaBattleAnimator.SetBool("MoveState", true);LunaBattleAnimator.SetFloat("MoveValue", -1);//运用0.5秒时间运动到monster前面一米LunaTrans.DOLocalMove(monsterInitPos + new Vector3(1, 0, 0), 0.5f).OnComplete(() =>{LunaBattleAnimator.SetBool("MoveState", false);LunaBattleAnimator.SetFloat("MoveValue", 0);LunaBattleAnimator.CrossFade("Attack", 0);//直接播放Attack动画,层级为0MonsterSr.DOFade(0.3f, 0.2f).OnComplete(() =>{JudgeMonsterHP(20);//透明度恢复正常});//是怪物在被攻击到后出现隐身效果,动画播放时长为0.2秒,透明度参数(0.3f)范围为(0-1.0f),表示透明程度,若不用此函数,其范围为0-255});yield return new WaitForSeconds(1.667f);MonsterTrans.DOLocalMove(monsterInitPos, 0.3f);LunaBattleAnimator.SetBool("MoveState", true);LunaBattleAnimator.SetFloat("MoveValue", 1);LunaTrans.DOLocalMove(LunaInitPos, 0.5f).OnComplete(()=> { LunaBattleAnimator.SetBool("MoveState", false); });//使luna变成Idle状态yield return new WaitForSeconds(0.8f);StartCoroutine(MonsterAttack());

现在仿照上面做出Monster的攻击行为

IEnumerator MonsterAttack(){UI_Manager.Instance.ShowOrHideBattlePanel(false);UI_Manager.Instance.ShowOrHideBattlePanel(false);MonsterTrans.DOLocalMove(LunaInitPos - new Vector3(1, 0, 0), 0.5f).OnComplete(() =>{LunaBattleAnimator.CrossFade("Hit", 0);//直接播放Hit动画,层级为0LunaSr.DOFade(0.3f, 0.2f).OnComplete(() =>{JudgeLunaHP(20);//透明度恢复正常});//是Luna在被攻击到后出现隐身效果,动画播放时长为0.2秒,透明度参数(0.3f)范围为(0-1.0f),表示透明程度,若不用此函数,其范围为0-255});yield return new WaitForSeconds(1.333f);LunaBattleAnimator.SetBool("MoveState", true);LunaBattleAnimator.SetFloat("MoveValue", 1);MonsterTrans.DOLocalMove(monsterInitPos, 0.5f).OnComplete(() => { LunaBattleAnimator.SetBool("MoveState", false); });//使luna变成Idle状态yield return new WaitForSeconds(0.5f);UI_Manager.Instance.ShowOrHideBattlePanel(true);}

现在开始制作Luna的防御行为

 public void LunaDefend(){StartCoroutine(PerformDefendLogic());}IEnumerator PerformDefendLogic(){UI_Manager.Instance.ShowOrHideBattlePanel(false);LunaBattleAnimator.SetBool("Defend", true);MonsterTrans.DOLocalMove(LunaInitPos - new Vector3(1.5f, 0, 0), 0.5f);yield return new WaitForSeconds(0.5f);LunaBattleAnimator.SetFloat("MoveValue", -1);//运用0.5秒时间运动到monster前面一米MonsterTrans.DOLocalMove(LunaInitPos, 0.2f).OnComplete(() =>{MonsterTrans.DOLocalMove(LunaInitPos - new Vector3(1.5f, 0, 0), 0.5f);LunaTrans.DOLocalMove(LunaInitPos + new Vector3(1f, 0, 0), 0.2f).OnComplete(() =>{LunaTrans.DOLocalMove(LunaInitPos, 0.2f);});});yield return new WaitForSeconds(0.4f);MonsterTrans.DOLocalMove(monsterInitPos, 0.3f).OnComplete(()=> {UI_Manager.Instance.ShowOrHideBattlePanel(true);LunaBattleAnimator.SetBool("Defend", false);});}

蓝耗判定与蓝量血量的增加减少方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Gamemanager : MonoBehaviour
{public static Gamemanager Instance;public int LunaHP;//最大生命值public int LunacurrentHP;//Luna的当前最大生命值public int LunaMP;//最大蓝量public int LunaCurrentMP;//当前蓝量/*public int Health { get { return LunacurrentHP; } }//返回LunacurrentHP*/public int MaxHP { get { return LunaHP; } }public GameObject BattleGo;//战斗场景,游戏物体public void Awake(){Instance = this;}/*public void ChangeHealth(int amount){LunacurrentHP = Mathf.Clamp(LunacurrentHP + amount, 0, LunaHP);Debug.Log(LunacurrentHP + "/" + LunaHP);}*/public void EnterOrExitBallte(bool enter = true){BattleGo.SetActive(enter);}//回血public void AddOrDecreaseHP(int value){LunacurrentHP += value;if (LunacurrentHP >= LunaHP){LunacurrentHP = LunaHP;}if (LunaCurrentMP <= 0){LunaCurrentMP = 0;}UI_Manager.Instance.SetHPValue((float)LunacurrentHP / LunaHP);}//回蓝public void AddOrDecreaseMP(int value){LunaCurrentMP += value;if (LunaCurrentMP >= LunaHP){LunaCurrentMP = LunaHP;}if (LunaCurrentMP <= 0){LunaCurrentMP = 0;}UI_Manager.Instance.SetMPValue((float)LunaCurrentMP / LunaMP);}//是否能使用技能public bool CanUseMP(int value){return LunaCurrentMP >= value;}
}

对话系统的UI与对话信息结构体

创建NPCDialog脚本控制Luna与NPC的对话(DialogInfo)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class NpcDialog : MonoBehaviour
{private List<DialogInfo[]> dialogInfoList;private int contentIndex;//控制每一段的每一条的索引public Animator animator;// Start is called before the first frame updatevoid Start(){dialogInfoList = new List<DialogInfo[]>(){new DialogInfo[]{new DialogInfo() {name="Luna",content="(,,・∀・)ノ゛hello,我是LuNa,你可以用上下左右控制我移动,空格键与NPC进行对话,战斗中需要简单点击按钮执行相应行为" }},new DialogInfo[]{new DialogInfo() {name="Nala",content="好久不见了,小猫咪(*ΦωΦ*),Luna~" },new DialogInfo() {name="Luna",content="好久不见,Nala,你还是那么有活力,哈哈" },new DialogInfo() {name="Nala",content="还好吧~" },new DialogInfo() {name="Nala",content="我的狗一直在叫,但是我这会忙不过来,你能帮我安抚一下它吗?" },new DialogInfo() {name="Luna",content="啊?" },new DialogInfo() {name="Nala",content="(,,´•ω•)ノ(´っω•`。)摸摸他就行,摸摸说呦西呦西,真是个好孩子呐" },new DialogInfo() {name="Nala",content="别看他叫的这么凶,其实他就是想引起别人的注意" },new DialogInfo() {name="Luna",content="可是。。。。" },new DialogInfo() {name="Luna",content="我是猫女郎啊" },new DialogInfo() {name="Nala",content="安心啦,不会咬你哒,去吧去吧~" },},new DialogInfo[]{new DialogInfo() {name="Nala",content="他还在叫呢" }},//3new DialogInfo[]{new DialogInfo() {name="Nala",content="感谢你呐,Luna,你还是那么可靠!" },new DialogInfo() {name="Nala",content="我想请你帮个忙好吗" },new DialogInfo() {name="Nala",content="说起来这事怪我。。。" },new DialogInfo() {name="Nala",content="今天我睡过头了,出门比较匆忙" },new DialogInfo() {name="Nala",content="然后装蜡烛的袋子口子没封好!o(╥﹏╥)o" },new DialogInfo() {name="Nala",content="结果就。。。蜡烛基本丢完了" },new DialogInfo() {name="Luna",content="你还是老样子,哈哈。。" },new DialogInfo() {name="Nala",content="所以,所以喽,你帮帮忙,帮我把蜡烛找回来" },new DialogInfo() {name="Nala",content="如果你能帮我找回全部的5根蜡烛,我就送你一把神器" },new DialogInfo() {name="Luna",content="神器?(¯﹃¯)" },new DialogInfo() {name="Nala",content="是的,我感觉很适合你,加油呐~" },},new DialogInfo[]{new DialogInfo() {name="Nala",content="你还没帮我收集到所有的蜡烛,宝~" },},//5new DialogInfo[]{new DialogInfo() {name="Nala",content="可靠啊!竟然一个不差的全收集回来了" },new DialogInfo() {name="Luna",content="你知道多累吗?" },new DialogInfo() {name="Luna",content="你到处跑,真的很难收集" },new DialogInfo() {name="Nala",content="辛苦啦辛苦啦" },new DialogInfo() {name="Nala",content="这是给你的奖励" },new DialogInfo() {name="Nala",content="蓝纹火锤,传说中的神器" },new DialogInfo() {name="Nala",content="应该挺适合你的" },new DialogInfo() {name="Luna",content="~~获得蓝纹火锤~~(遇到怪物可触发战斗)" },new DialogInfo() {name="Luna",content="哇,谢谢你!Thanks♪(・ω・)ノ" },new DialogInfo() {name="Nala",content="嘿嘿(*^▽^*),咱们的关系不用客气" },new DialogInfo() {name="Nala",content="正好,最近山里出现了一堆怪物,你也算为民除害,帮忙清理5只怪物" },new DialogInfo() {name="Luna",content="啊?" },new DialogInfo() {name="Luna",content="这才是你的真实目的吧?!" },new DialogInfo() {name="Nala",content="拜托拜托啦,否则真的很不方便我卖东西" },new DialogInfo() {name="Luna",content="无语中。。。" },new DialogInfo() {name="Nala",content="求求你了,啵啵~" },new DialogInfo() {name="Luna",content="哎,行吧,谁让你大呢~" },new DialogInfo() {name="Nala",content="嘻嘻,那辛苦宝子啦" }},new DialogInfo[]{new DialogInfo() {name="Nala",content="宝,你还没清理干净呢,这样我不方便嘛~" },},new DialogInfo[]{new DialogInfo() {name="Nala",content="真棒,luna,周围的居民都会十分感谢你的,有机会来我家喝一杯吧~" },new DialogInfo() {name="Luna",content="我觉得可行,哈哈~" }},new DialogInfo[]{new DialogInfo() {name="Nala",content="改天再见喽~" },}};Gamemanager.Instance.dialogInfoIndex = 0;contentIndex = 1;}// Update is called once per framevoid Update(){}public void DisplayDialog(){if (Gamemanager.Instance.dialogInfoIndex > 7){return;}if (contentIndex >= dialogInfoList[Gamemanager.Instance.dialogInfoIndex].Length){//当前对话结束,可以控制LunacontentIndex = 0;UI_Manager.Instance.ShowDialog();Gamemanager.Instance.canControlLuna = true;}else{DialogInfo dialogInfo = dialogInfoList[Gamemanager.Instance.dialogInfoIndex][contentIndex];UI_Manager.Instance.ShowDialog(dialogInfo.content, dialogInfo.name);contentIndex++;animator.SetTrigger("Talk");}}
}
public struct DialogInfo
{public string name;public string content;
}

创建Talk函数控制Luna在与Nala对话时的动作(在LunaController下进行设置)

public void Talk(){//设置触发器检测的半径Collider2D collider = Physics2D.OverlapCircle(rigidbody.position,0.5f, LayerMask.GetMask("NPC"));if (collider != null){if (collider.name == "Nala"){Gamemanager.Instance.canControlLuna = false;collider.GetComponent<NpcDialog>().DisplayDialog();}else if (collider.name == "Dog"&& !Gamemanager.Instance.hasPetTheDog &&Gamemanager.Instance.dialogInfoIndex == 2){PetTheDog();Gamemanager.Instance.canControlLuna = false;collider.GetComponent<Dog>().BeHappy();}}}

其中Physics2D.OverlapCircle控制检测半径,LayerMask.GetMask("NPC")表示只有在触碰到层级为NPC的游戏物体才会进行触发

层级设置如下

并且在LunaController中的update函数中编写如下脚本

void Update(){//监听玩家输入float horizontal = Input.GetAxis("Horizontal");//水平float vertical = Input.GetAxis("Vertical");//垂直move = new Vector2(horizontal, vertical);animator.SetFloat("MoveValues", 0);/*Debug.Log(horizontal);*///当前玩家输入某个轴向不为0if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0)){//判断参数近似相等lookDirection.Set(move.x, move.y);lookDirection.Normalize();//单位化/*animator.SetFloat("MoveValues", 1);*/}//动画控制moveScale = move.magnitude;if(move.magnitude>0){if(Input.GetKey(KeyCode.LeftShift)){moveScale = 1;movespeed = initspeed/2;}else{moveScale = 2;movespeed = initspeed;}}animator.SetFloat("MoveValues", moveScale);animator.SetFloat("LookX", lookDirection.x);animator.SetFloat("LookY", lookDirection.y);//按下空格可以进行交互if (Input.GetKeyDown(KeyCode.Space)){Talk();}}

实现对话之间的跳转

public void DisplayDialog(){if (Gamemanager.Instance.dialogInfoIndex > 7){return;}if (contentIndex >= dialogInfoList[Gamemanager.Instance.dialogInfoIndex].Length){if (Gamemanager.Instance.dialogInfoIndex == 2 &&!Gamemanager.Instance.hasPetTheDog){}else if (Gamemanager.Instance.dialogInfoIndex == 4 &&Gamemanager.Instance.candleNum < 5){}else if (Gamemanager.Instance.dialogInfoIndex == 6 &&Gamemanager.Instance.killNum < 5){}else{Gamemanager.Instance.dialogInfoIndex++;}//在完成前面的任务以后进行清怪任务if (Gamemanager.Instance.dialogInfoIndex == 6){Gamemanager.Instance.ShowMonsters();}contentIndex = 0;UI_Manager.Instance.ShowDialog();Gamemanager.Instance.canControlLuna = true;}else{DialogInfo dialogInfo = dialogInfoList[Gamemanager.Instance.dialogInfoIndex][contentIndex];UI_Manager.Instance.ShowDialog(dialogInfo.content, dialogInfo.name);contentIndex++;animator.SetTrigger("Talk");//使只有Luna在触碰到Nala并且有对话时,Nala才会进行Talk动画}}

任务一:安抚狗狗

在Dog物体上设置Dog脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Dog : MonoBehaviour
{private Animator animator;public GameObject starEffect;public AudioClip petSound;// Start is called before the first frame updatevoid Start(){animator = GetComponent<Animator>();}public void BeHappy(){animator.CrossFade("Comfortable", 0);Gamemanager.Instance.hasPetTheDog = true;Gamemanager.Instance.SetContentIndex();Destroy(starEffect);Gamemanager.Instance.PlaySound(petSound);Invoke("CanControlLuna", 2.5f);}private void CanControlLuna(){Gamemanager.Instance.canControlLuna = true;}
}

任务二:收集蜡烛以及清楚怪物任务

在Candle脚本中进行如下设置

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Candle : MonoBehaviour
{public GameObject effectGo;private void OnTriggerEnter2D(Collider2D collision){Gamemanager.Instance.candleNum++;/* Debug.Log(collision);*/Gamemanager.Instance.AddOrDecreaseHP(1);Destroy(gameObject);Instantiate(effectGo, transform.position, Quaternion.identity);/*LunaController luna = collision.GetComponent<LunaController>();Gamemanager.Instance.ChangeHealth(1);*/if (Gamemanager.Instance.candleNum >= 5){Gamemanager.Instance.SetContentIndex();}Destroy(gameObject);}
}

对函数进行如下改动

Gamemanager:

public void EnterOrExitBallte(bool enter = true,int addkillNum = 0){UI_Manager.Instance.ShowOrHideBattlePanel(enter);BattleGo.SetActive(enter);enterBattle = enter;if (!enter)//非战斗状态{killNum += addkillNum;if (addkillNum > 0){DestoryMonster();}MonsterCurrentHP = 50;if (LunaCurrentMP <= 0){LunacurrentHP = 100;LunaCurrentMP = 0;battleMonsterGo.transform.position += new Vector3(0, 2, 0);}}}

BattleController

private void JudgeMonsterHP(int value){if (Gamemanager.Instance.AddOrDecreaseMonsterHP(value) <= 0){Gamemanager.Instance.EnterOrExitBallte(false , 1);}else{MonsterSr.DOFade(1, 0.4f);}}

现在插入背景音乐

Gamemanager

public AudioSource audioSource;//音乐组件
public AudioClip normalClip;
public AudioClip battleClip;
public void PlayMusic(AudioClip audioClip){if (audioSource.clip != audioClip){audioSource.clip = audioClip;audioSource.Play();}}public void PlaySound(AudioClip audioClip){if (audioClip){audioSource.PlayOneShot(audioClip);}}
public void EnterOrExitBallte(bool enter = true,int addkillNum = 0){UI_Manager.Instance.ShowOrHideBattlePanel(enter);BattleGo.SetActive(enter);enterBattle = enter;if (!enter)//非战斗状态{killNum += addkillNum;if (addkillNum > 0){DestoryMonster();}MonsterCurrentHP = 50;PlayMusic(normalClip);if (LunaCurrentMP <= 0){LunacurrentHP = 100;LunaCurrentMP = 0;battleMonsterGo.transform.position += new Vector3(0, 2, 0);}}else//战斗状态{PlayMusic(battleClip); }}

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

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

相关文章

95.网络游戏逆向分析与漏洞攻防-ui界面的设计-ui的设计与架构

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

基于jeecgboot-vue3的Flowable新建流程定义(二)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 接上一节 4、编辑好后进行保存&#xff0c;保存代码如下&#xff1a; /*保存流程定义*/const save (data: any) > {//console.log("save data", data); // { process: {..…

博客增长与数据分析:不可不知的 6 大策略

CSDN 的朋友你们好&#xff0c;我是何未来&#xff0c;一个热爱编程和写作的计算机本科生&#xff0c;今天给大家带来专栏【程序员博主教程&#xff08;完全指南&#xff09;】的第 11 篇文章“分析和追踪博客表现”。本篇文章为你揭示了如何通过数据洞察来优化你的技术博客&am…

-bash: locate: 未找到命令(解决办法)

-bash: locate: 未找到命令的解决办法 一、解决办法二、什么是locate三 、locate命令的具体用法 一、解决办法 CentOS7默认没有安装locate命令&#xff0c;安装方式如下&#xff1a; 执行以下命令进行安装&#xff1a; yum install mlocate用 updatedb 指令创建 或更新locate …

数学建模--LaTeX基本介绍和入门

1.引言 &#xff08;1&#xff09;上次我们介绍到了我们这个团队第一次参加这个数学建模比赛&#xff0c;就是这个电工杯&#xff0c;我是一名论文手&#xff0c;我们在这个下午也是对于这个比赛过程中出现的问题做了相应的分析&#xff0c;每个人也是进行了反思&#xff0c;知…

Tomcat源码解析(七):底层如何获取请求url、请求头、json数据?

Tomcat源码系列文章 Tomcat源码解析(一)&#xff1a;Tomcat整体架构 Tomcat源码解析(二)&#xff1a;Bootstrap和Catalina Tomcat源码解析(三)&#xff1a;LifeCycle生命周期管理 Tomcat源码解析(四)&#xff1a;StandardServer和StandardService Tomcat源码解析(五)&…

最大连续1的个数(滑动窗口)

算法原理&#xff1a; 这道题大眼一看是关于翻转多少个0的问题&#xff0c;但是&#xff0c;如果你按照这种思维去做题&#xff0c;肯定不容易。所以我们要换一种思维去做&#xff0c;这种思维不是一下就能想到的&#xff0c;所以想不到也情有可原。 题目是&#xff1a;给定一…

Vue3:动态路由+子页面(新增、详情页)动态路由配置(代码全注释)

文章目录 实现思路调用后端接口获取用户权限获取页面权限动态绑定到路由对象中动态添加子页面路由 实现思路 emm&#xff0c;项目中使用动态路由实现根据后端返回的用户详情信息&#xff0c;动态将该用户能够访问的页面信息&#xff0c;动态生成并且绑定到路由对象中。但是后…

如何从清空的回收站中恢复已删除的Excel文件?

“嗨&#xff0c;几天前我删除了很多没有备份的Excel文件。回收站已清空。当我意识到我犯了一个大错误时&#xff0c;所有的Excel文件都消失了&#xff0c;回收站里什么都没有。清空回收站后是否可以恢复已删除的 Excel 文件&#xff1f; 回收站是一种工具&#xff0c;可让您在…

【openlayers系统学习】4.2Mapbox 样式渲染图层

二、Mapbox 样式渲染图层 显然我们目前的地图需要一些样式。 VectorTile​ 图层的样式与 Vector​ 图层的样式工作方式完全相同。那里描述的样式在这里也适用。 对于这样的地图&#xff0c;创建数据驱动的样式&#xff08;对矢量图层操作&#xff09;非常简单。但矢量切片也用…

单兵组网设备+指挥中心:集群系统技术详解

一、单兵设备功能特点 单兵组网设备是现代通信技术的重要成果&#xff0c;旨在为单个作战或工作单元提供高效的通信和数据传输能力。其主要功能特点包括&#xff1a; 1. 便携性&#xff1a;设备轻巧&#xff0c;便于单兵携带和使用&#xff0c;适应各种复杂环境。 2. 通信能…

PHP之fastadmin系统配置分组增加配置和使用

目录 一、实现功能&#xff1a;fasttadmin实现添加系统配置分组和添加参数、使用 二、添加分组 三、配置分组参数 四、最终存储位置 五、获取配置参数 一、实现功能&#xff1a;fasttadmin实现添加系统配置分组和添加参数、使用 二、添加分组 在字典配置中找到分组对应键值…

linux系统——top资源管理器

在linux系统中&#xff0c;有类似于windows系统中的资源管理器&#xff0c;top用于实时的监控系统的任务执行状态以及硬件配置信息 在linux中&#xff0c;输入top命令&#xff0c;可以进入相应界面&#xff0c;在此界面可以使用一些指令进行操作 如&#xff0c;输入z 可以改变…

终端安全管理系统、天锐DLP(数据泄露防护系统)| 数据透明加密保护,防止外泄!

终端作为企业员工日常办公、数据处理和信息交流的关键工具&#xff0c;承载着企业运营的核心信息资产。一旦终端安全受到威胁&#xff0c;企业的敏感数据将面临泄露风险&#xff0c;业务流程可能遭受中断&#xff0c;甚至整个企业的运营稳定性都会受到严重影响。 因此&#xff…

【EVI】Hume AI 初探

写在前面的话 Hume AI宣布已在B轮融资中筹集5000万美元&#xff0c;由前Google DeepMind研究员Alan Cowen创立并担任CEO。该AI模型专注于理解人类情感&#xff0c;并发布了「共情语音界面」演示&#xff0c;通过语音对话实现互动。从 Hume AI 官网展示的信息&#xff0c;EVI 能…

力扣刷题--747. 至少是其他数字两倍的最大数【简单】

题目描述 给你一个整数数组 nums &#xff0c;其中总是存在 唯一的 一个最大整数 。 请你找出数组中的最大元素并检查它是否 至少是数组中每个其他数字的两倍 。如果是&#xff0c;则返回 最大元素的下标 &#xff0c;否则返回 -1 。 示例 1&#xff1a; 输入&#xff1a;n…

Python-opencv通过距离变换提取图像骨骼

文章目录 距离变换distanceTransform函数 距离变换 如果把二值图像理解成地形&#xff0c;黑色表示海洋&#xff0c;白色表示陆地&#xff0c;那么陆地上任意一点&#xff0c;到海洋都有一个最近的距离&#xff0c;如下图所示&#xff0c;对于左侧二值图像来说&#xff0c;【d…

漂流瓶挂机项目,聊天脚本赚钱新玩法,号称单机30-50+ (教程+软件)

一、项目简介&#xff1a; 漂流瓶挂机项目主要是通过使用探遇漂流瓶、音麦漂流瓶等聊天软件&#xff0c;为用户提供一个聊天赚钱的平台。男性用户需要充值后才能发送消息&#xff0c;而女性用户则可以通过接收消息赚取分红。男性用户发送给女性用户的消息费用大约在.1-.2元之间…

VScode中对git的学习笔记

1.git是什么&#xff1f; Git是一个功能强大的分布式版本控制系统&#xff0c;由Linux内核的创始人Linus Torvalds在2005年创建。它以其速度、数据完整性和支持大型项目的能力而闻名&#xff0c;被广泛应用于软件开发中。Git允许开发者在本地机器上拥有完整的代码库副本&#x…

SashulinMessageBroker:在消息流中调用C++ DLL

一、背景 在现实应用中&#xff0c;算法、核心逻辑为了追求快速高效的运行速度&#xff0c;很多人都采用C来编写&#xff0c;并打包成动态库供外部使用。SMB针对这种应用场景&#xff0c;提供了DLL组件&#xff0c;实现在消息流中对DLL的动态调用。下实例讲解如何实现DLL as S…