这一章将设置敌人的来回走动和伤害区域
1.重置Ruby的健康值 Reset Ruby’s Health
在Assets > Art > Sprites > Environment中,找到Damageable精灵。
从Damageable Sprite中新建一个游戏对象。
在层次窗口中,拖动精灵,打开新的游戏对象,增加Sprite Renderer 组件。
给Damageable GameObject增加Box Collider 2D,选中Is Trigger。
建立一个新的脚本,名为DamageZone。
public class DamageZone : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
RubyController controller = other.GetComponent<RubyController >();
if (controller != null)
{
controller.ChangeHealth(-1);
}
}
}
在ChangeHealth方法中,传的是-1,表示将对Ruby的健康值减1,受到了伤害。
将OnTriggerEnter2D 改为 OnTriggerStay2D方法,将会使Ruby一直受到伤害。
为了解决静止时,没有受到伤害,修改如图,将Sleeping Mode 改为 Never Sleep。
在Ruby方法中,有2秒的保护时间,在两秒内Ruby不被伤害。每两秒伤害一次。
public class RubyController : MonoBehaviour
{
public float speed = 3.0f;
public int maxHealth = 5;
public float timeInvincible = 2.0f;
public int health { get { return currentHealth; }}
int currentHealth;
bool isInvincible;
float invincibleTimer;
Rigidbody2D rigidbody2d;
float horizontal;
float vertical;
// Start is called before the first frame update
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
currentHealth = maxHealth;
}
// Update is called once per frame
void Update()
{
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
if (isInvincible)
{
invincibleTimer -= Time.deltaTime;
if (invincibleTimer < 0)
isInvincible = false;
}
}
void FixedUpdate()
{
Vector2 position = rigidbody2d.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;
rigidbody2d.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);
}
}
2.图形旁注 Graphic sidenote
首先设置游戏对象的scale为1,1,1。在Sprite Renderer component中,将Draw Mode设置为Tiled,并Tile Mode为Adaptive。
如出现如下的警告
则从工程窗口中,修改Damageable Sprite的Mesh Type为Full Rect。
在视图的最底部点应用。
但是Collider没有按照比例缩放。设置下Box Collider 2D的Auto Tiling属性。
3.敌人 Enemies
将上面的敌人图像复制到Scene文件夹中。增加Rigidbody2D 和BoxCollider2D属性,别忘记了,将gravity 设置为0。
4.前进和后退 Back and Forth
建立一个新的脚本EnemyController。
public class EnemyController2 : MonoBehaviour
{
public float speed;
Rigidbody2D rigidbody2D;
// Start is called before the first frame update
void Start()
{
rigidbody2D = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
Vector2 position = rigidbody2D.position;
position.x = position.x + Time.deltaTime * speed;
rigidbody2D.MovePosition(position);
}
}
如果要来回走的话,则要增加更多的变量
public class EnemyController : MonoBehaviour
{
public float speed;
public bool vertical;
public float changeTime = 3.0f;
Rigidbody2D rigidbody2D;
float timer;
int direction = 1;
// Start is called before the first frame update
void Start()
{
rigidbody2D = GetComponent<Rigidbody2D>();
timer = changeTime;
}
void Update()
{
timer -= Time.deltaTime;
if (timer < 0)
{
direction = -direction;
timer = changeTime;
}
}
void FixedUpdate()
{
Vector2 position = rigidbody2D.position;
if (vertical)
{
position.y = position.y + Time.deltaTime * speed * direction;;
}
else
{
position.x = position.x + Time.deltaTime * speed * direction;;
}
rigidbody2D.MovePosition(position);
}
}
在start方法中,初始化了时间变量。更新方法中,通过timer更新敌人的方向。
5.伤害 Damage
void OnCollisionEnter2D(Collision2D other)
{
RubyController player = other.gameObject.GetComponent<RubyController>();
if (player != null)
{
player.ChangeHealth(-1);
}
}
当在冲突范围时,测试Ruby是否已进入冲突区域,如进入,则对Ruby进行伤害。
现在可以将多个敌人拖到工程窗口中了。
6.完整的脚本 Check Your Scripts
public class RubyController : MonoBehaviour
{
public float speed = 3.0f;
public int maxHealth = 5;
public float timeInvincible = 2.0f;
public int health { get { return currentHealth; }}
int currentHealth;
bool isInvincible;
float invincibleTimer;
Rigidbody2D rigidbody2d;
float horizontal;
float vertical;
// Start is called before the first frame update
void Start()
{
rigidbody2d = GetComponent<Rigidbody2D>();
currentHealth = maxHealth;
}
// Update is called once per frame
void Update()
{
horizontal = Input.GetAxis("Horizontal");
vertical = Input.GetAxis("Vertical");
if (isInvincible)
{
invincibleTimer -= Time.deltaTime;
if (invincibleTimer < 0)
isInvincible = false;
}
}
void FixedUpdate()
{
Vector2 position = rigidbody2d.position;
position.x = position.x + speed * horizontal * Time.deltaTime;
position.y = position.y + speed * vertical * Time.deltaTime;
rigidbody2d.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);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamageZone : MonoBehaviour
{
void OnTriggerStay2D(Collider2D other)
{
RubyController controller = other.GetComponent<RubyController >();
if (controller != null)
{
controller.ChangeHealth(-1);
}
}
}
public class EnemyController : MonoBehaviour
{
public float speed;
public bool vertical;
public float changeTime = 3.0f;
Rigidbody2D rigidbody2D;
float timer;
int direction = 1;
// Start is called before the first frame update
void Start()
{
rigidbody2D = GetComponent<Rigidbody2D>();
timer = changeTime;
}
void Update()
{
timer -= Time.deltaTime;
if (timer < 0)
{
direction = -direction;
timer = changeTime;
}
}
void FixedUpdate()
{
Vector2 position = rigidbody2D.position;
if (vertical)
{
position.y = position.y + Time.deltaTime * speed * direction;;
}
else
{
position.x = position.x + Time.deltaTime * speed * direction;;
}
rigidbody2D.MovePosition(position);
}
void OnCollisionEnter2D(Collision2D other)
{
RubyController player = other.gameObject.GetComponent<RubyController >();
if (player != null)
{
player.ChangeHealth(-1);
}
}
}