在上一次的游戏制作中,我们了解了如何使用Rigidbody组件和Collider组件,并能实现碰撞的物理效果和角色的移动。
本次我们将增加游戏的玩法:
- 添加角色生命值系统
- 增加生命包
- 添加陷阱和敌人
大家可以搭配这两篇的内容先自行实现一下哦~
Unity入门学习(四)https://blog.csdn.net/m0_63552826/article/details/134313592?spm=1001.2014.3001.5501Unity入门学习(五)https://blog.csdn.net/m0_63552826/article/details/134336472?spm=1001.2014.3001.5501
添加生命值系统
打开角色Control脚本,我们设置两个变量,一个公有变量maxHealth和另一个私有变量currentHealth,这里的设置maxHealth变量一个是方便在Unity直接调试,另一个是对currentHealth进行调控,防止出现生命值超过我们预定的最大生命值的情况。
修改生命值系统
设置好两个变量之后,我们就要编写相关函数来对生命值进行修改。
在这里我们需要注意定义属性的用法!!!
public class Control : MonoBehaviour
{
public int maxHealth = 3;
int currentHealth;
public int health { get { return currentHealth; } }
public float speed = 5f;
public GameObject text;
public GameObject exit;
Rigidbody2D rb;
float horizontal;
float vertical;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
text.SetActive(false);
currentHealth = maxHealth;
}
// Update is called once per frame
void Update()
{
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
if (transform.position.x < exit.transform.position.x + 0.5f && exit.transform.position.x - 0.5f < transform.position.x && transform.position.y < exit.transform.position.y + 0.5f && exit.transform.position.y - 0.5f < transform.position.y)
{
text.SetActive(true);
}
}
private void FixedUpdate()
{
Vector2 position = rb.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;
rb.MovePosition(position);
}
public void ChangeHealth( int amount)
{
currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
Debug.Log("生命值:" + currentHealth + "/" + maxHealth);
}
}
添加生命包
编写好相应代码之后我们开始创建增加我们生命值的生命包,在这里我们需要创建一个可收集的游戏对象。
- 在Hierarchy窗口中右键Create Empty并重命名为HealthBox。
- 再在Hierarchy窗口中右键2D Object > Sprites > Triangle 并重命名为Health,命名完之后拖到到HealthBox里,称为HealthBox子类。
- 点击Health,在Inspector界面添加Box Collider2D组件并启动IsTrigger属性。(注意这里大家可以点击Edit Collider,此时看到Health会有绿色的框,这个是碰撞体的边界,大家可以适当进行大小调整。)
- 在Health的Inspector界面改变颜色进行装饰。
编写脚本
此时我们需要创建生命包Health的脚本。
- 在Project窗口中,选择Assets > Scripts,右键选择Create > C# Script。
- 重命名脚本为HealthCollect。在Hierarchy中,选择Health游戏对象,将HealthCollect脚本从Project窗口中拖放到Inspector,从而将这个脚本作为组件挂载到游戏对象上。
- 双击脚本文件,删除Start和Update函数,因为我们不需要再游戏开始时或者游戏每一帧中处理任何事情。
HealthCollect代码如下:
public class HealthCollect : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D collision)
{
Control player = collision.GetComponent<Control>();
if(player != null)
{
if(player.health < player.maxHealth)
{
player.ChangeHealth(1);
Destroy(gameObject);
}
}
}
}
添加陷阱
陷阱的使用与前面的生命包有些类似,但是要注意的就是角色在伤害区域上会一直受到伤害后并且需要角色的控制无敌时间。
Damage代码:
public class Damage : MonoBehaviour
{
// Start is called before the first frame update
private void OnTriggerStay2D(Collider2D collision)
{
Control player = collision.GetComponent<Control>();
if(player != null)
{
player.ChangeHealth(-1);
}
}
}
同时因为伤害区域的存在,角色的代码也需要改动:添加无敌时间和判定。
角色代码:
public class Control : MonoBehaviour
{
public int maxHealth = 3;
int currentHealth;
public float timeInvincible = 2.0f;
bool isInvincible;
float invincibleTimer;
public int health { get { return currentHealth; } }
public float speed = 5f;
public GameObject text;
public GameObject exit;
Rigidbody2D rb;
float horizontal;
float vertical;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
text.SetActive(false);
currentHealth = maxHealth;
}
// Update is called once per frame
void Update()
{
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
if (transform.position.x < exit.transform.position.x + 0.5f && exit.transform.position.x - 0.5f < transform.position.x && transform.position.y < exit.transform.position.y + 0.5f && exit.transform.position.y - 0.5f < transform.position.y)
{
text.SetActive(true);
}
if (isInvincible)
{
invincibleTimer -= Time.deltaTime;
if(invincibleTimer < 0)
{
isInvincible = false;
}
}
}
private void FixedUpdate()
{
Vector2 position = rb.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;
rb.MovePosition(position);
}
public void ChangeHealth( int amount)
{
if(amount < 0)
{
if (isInvincible)
{
return;
}
isInvincible = true;
invincibleTimer = timeInvincible;
}
currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
Debug.Log("生命值:" + currentHealth + "/" + maxHealth);
}
}
添加敌人
创建一个敌人脚本,相比较陷阱,敌人需要Rigidbody2D组件且需要让敌人进行移动,不仅如此还需要设置一个计时器让敌人旋转方向移动。
public class Enemy : MonoBehaviour
{
public float speed;
public bool vertical;
public float changTime = 2.0f;
Rigidbody2D rb;
float time;
int direction = -1;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
time = changTime;
}
// Update is called once per frame
void Update()
{
time -= Time.deltaTime;
if(time < 0)
{
direction = -direction;
time = changTime;
}
}
private void FixedUpdate()
{
Vector2 pos = rb.position;
pos.y = pos.y + speed * Time.deltaTime * direction;
rb.MovePosition(pos);
}
private void OnCollisionEnter2D(Collision2D collision)
{
Control player = collision.gameObject.GetComponent<Control>();
if(player != null)
{
player.ChangeHealth(-1);
}
}
}
好!到这里我们的游戏稍微进行了一些装饰,不过我们还需要增加一点,那就是当角色的生命值归0时提示Game Over。这个在上次说明了用文本信息就可以实现。
游戏结束
public class Control : MonoBehaviour
{
public int maxHealth = 3;
int currentHealth;
public float timeInvincible = 2.0f;
bool isInvincible;
float invincibleTimer;
public int health { get { return currentHealth; } }
public float speed = 5f;
public GameObject text;
public GameObject text_over;
public GameObject exit;
Rigidbody2D rb;
float horizontal;
float vertical;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
text.SetActive(false);
text_over.SetActive(false);
currentHealth = maxHealth;
}
// Update is called once per frame
void Update()
{
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
if (transform.position.x < exit.transform.position.x + 0.5f && exit.transform.position.x - 0.5f < transform.position.x && transform.position.y < exit.transform.position.y + 0.5f && exit.transform.position.y - 0.5f < transform.position.y)
{
text.SetActive(true);
}
if (isInvincible)
{
invincibleTimer -= Time.deltaTime;
if(invincibleTimer < 0)
{
isInvincible = false;
}
}
if(currentHealth <= 0)
{
text_over.SetActive(true);
}
}
private void FixedUpdate()
{
Vector2 position = rb.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;
rb.MovePosition(position);
}
public void ChangeHealth( int amount)
{
if(amount < 0)
{
if (isInvincible)
{
return;
}
isInvincible = true;
invincibleTimer = timeInvincible;
}
currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
Debug.Log("生命值:" + currentHealth + "/" + maxHealth);
}
}