目录
1 实例概述
1.1 玩家脚本
1.2 触发器脚本
2 通过当前血量判断是否销毁血包
2.1 将private修改成public实现跨类调用
运行效果
2.2 使用internal修饰符
运行效果
2.3 将血量封装起来,定义属性访问
该方法优化点在于
3 C#中get和set访问器
3.1 定义属性
get{} 取值器
set{} 赋值器
3.2 自动实现的属性
3.3 结合案例
学习的教程
【unity2021入门教程】68-2D游戏开发教程系列-03-RubyAdventure2DRpg官方教程-16-完善触发器代码_哔哩哔哩_bilibili
为什么会写这么一篇博客呢?在跟着教程学习制作RubyAdventure项目的过程中,进行到了“吃血包加血”的脚本编写,其中涉及到需要在“血包”类脚本中调用“玩家Ruby”脚本里的成员变量的操作,其中在进行脚本优化时就涉及到了封装、对数据成员保护的概念,正好有一个参考案例,就打算记录一下C#封装的实操。
1 实例概述需要实现玩家Ruby在场景中移动,吃掉“血包”草莓并加血的操作。
1.1 玩家脚本当前项目中玩家挂了一个脚本,里面写入了赋予的最大生命值maxHealth和当前生命值currentHealth,全部脚本如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RubyController : MonoBehaviour
{
//设置最大生命值(生命上限)
public int maxHealth = 5;
//当前生命值,默认为0
private int currentHealth;
private void Start()
{
//初始化当前生命值
currentHealth = maxHealth;
}
// 每帧都会执行一次Update函数
void Update()
{
...
}
//更改生命值
//amount是游戏中加血/减血的操作
void ChangeHealth(int amount)
{
//限制当前生命值范围为[0,maxHealth]
currentHealth = Mathf.Clamp(currentHealth + amount, 0, maxHealth);
//输出
Debug.Log("当前生命值为:" + currentHealth + "/" + maxHealth);
}
}
1.2 触发器脚本
在游戏场景中,我们加入了一个游戏对象CollectibleHealth(“血包”,以下简称草莓),挂了一个2D碰撞体组件,勾选Is Trigger当作触发器使用。
给草莓挂一个脚本,通过添加碰撞检测后调用的OnTriggerEnter()内容实现
- 血包被玩家碰到后被吃掉
- 玩家血量增加1
- Debug返回指定语句
的游戏情景。
脚本具体如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HealthCollectible : MonoBehaviour
{
//设置每次碰撞加的血量
public int amount = 1;
private int CollideTimes;
//添加触发器事件,每次碰撞触发器时执行其中的代码
//其中,在这里对于草莓来说,other指的就是玩家Ruby
private void OnTriggerEnter2D(Collider2D other)
{
CollideTimes++;//碰撞次数+1
Debug.Log($"和当前物体发生碰撞的是:{other},当前是第{CollideTimes}次发生碰撞!");
//获取Ruby对象的脚本组件,还是用GetComponent方法
RubyController rubyController=other.GetComponent();
if(rubyController != null)
{
//血量+1
rubyController.ChangeHealth(amount);
//销毁血包
Destroy(gameObject);
}
else
{
Debug.LogError("未获取到当前游戏对象");
}
}
}
2 通过当前血量判断是否销毁血包
2.1 将private修改成public实现跨类调用
由于在Ruby脚本中,当前血量currentHealth的修饰符是private,为了实现跨类调用,选择直接把currentHealth暴露出来,变成public,就可以调用了,于是代码分别做以下修改:
- RubyController类
//设置最大生命值(生命上限)
public int maxHealth = 5;
//当前生命值,默认为0
//将生命值暴露出来,private -> public
public int currentHealth;
- HealthCollectible类
if(rubyController != null)
{
//血量+1
rubyController.ChangeHealth(amount);
if(rubyController.currentHealth < rubyController.maxHealth)
{
//销毁血包
Destroy(gameObject);
}
}
else
{
Debug.LogError("未获取到当前游戏对象");
}
运行效果
在项目中,currentHealth这个本应该是内部的变量被暴露在了项目属性栏中。
与public相比,用internal修饰符的成员变量可以在同一应用程序集内部访问。而public可以跨程序集访问。
同一程序集中,二者效果相同。
同时,在Unity中,public会将变量同时暴露在inspector属性栏中,internal则不会。
对应的修改成:
- RubyController类
//设置最大生命值(生命上限)
public int maxHealth = 5;
//当前生命值,默认为0
//private -> internal
internal int currentHealth;
- HealthCollectible类
if(rubyController != null)
{
//血量+1
rubyController.ChangeHealth(amount);
if(rubyController.currentHealth < rubyController.maxHealth)
{
//销毁血包
Destroy(gameObject);
}
}
else
{
Debug.LogError("未获取到当前游戏对象");
}
运行效果
可以看到,属性栏中是不会将currentHealth展示出来的。
但是,我认为无论是internal还是public,都是十分不安全的!这给了任何人可以随意修改内部值——玩家血量的机会,不利于项目的维护。
2.3 将血量封装起来,定义属性访问这里就涉及到一个面向对象程序设计的概念——封装,对于封装,我的另一篇博客有简单的介绍,这里就不再赘述:【Unity入门计划】了解C#或Unity中的类和对象_flashinggg的博客-CSDN博客
直接介绍方法,同样还是修改两个类:
- RubyController
//设置最大生命值(生命上限)
public int maxHealth = 5;
//当前生命值,默认为0
public int currentHealth;
//C#中支持面向对象程序设计中的封装,实现对数据成员进行保护
//数据成员变量本身是私有的,只能通过某一种方法或者属性访问
//属性是共有的,可以通过取值器--get,赋值器--set,设定对应字段的访问规则
//满足规则才能访问该成员变量
public int health { get { return currentHealth; } }
这里用到了get——取值器,后面会详细介绍C#的访问器。
- HealthCollectible
if(rubyController != null)
{
//血量+1
rubyController.ChangeHealth(amount);
if (rubyController.health
关注
打赏