[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 21회차 미션 시작합니다.
02 디아블로 게임 - 37. 38 챕터를 진행합니다.
이번 시간은 RPG 게임 플레이 시스템 구현하기 입니다.
+ 플레이어 속성 - 아이템 착용 상태에 따른 속성 변경
+ 플레이어 UI
+ 다이얼로그 시스템 구현 - 대화 시스템
+ 퀘스트 시스템 구현
+ 환경 장치(함정/문) 구현 - 레벨에 따른..
중요한 부분은 Attribute와 ModifiableInt 클래스 입니다.
+ Attribute - ModifiableInt를 가지고 Value를 설정
+ ModifiableInt - Base값을 가지고 변경되어진 값을 사용하는 Value(?)
+ AttributeType - 속성의 Type 설정
+ StatsObject - Attribute를 설정
이제 스크립트를 구현합니다. 바로 로직구현으로 고고고~~ ㅎㅎ
[Serializable]
public class ModifiableInt_New
{
[NonSerialized]
private int baseValue;
[SerializeField]
private int modifiedValue;
public int BaseValue {
get => baseValue;
set {
baseValue = value;
UpdateModifiedValue();
}
}
public int ModifiedValue {
get => modifiedValue;
set => modifiedValue = value;
}
// 변경되었다는 이벤트를 받기 위한 처리
private event Action<ModifiableInt_New> OnModifiedValue;
private List<IModifier> modifiers = new List<IModifier>();
public ModifiableInt_New(Action<ModifiableInt_New> method = null) {
ModifiedValue = baseValue;
RegisterModEvent(method);
}
public void RegisterModEvent(Action<ModifiableInt_New> method) {
if (method != null) {
OnModifiedValue += method;
}
}
public void UnregisterModEvent(Action<ModifiableInt_New> method) {
if (method != null) {
OnModifiedValue -= method;
}
}
private void UpdateModifiedValue() {
int valueToAdd = 0;
foreach (IModifier modifier in modifiers) {
modifier.AddValue(ref valueToAdd);
}
ModifiedValue = baseValue + valueToAdd;
OnModifiedValue?.Invoke(this);
}
public void AddModifier(IModifier modifier) {
modifiers.Add(modifier);
UpdateModifiedValue();
}
public void RemoveModifier(IModifier modifier) {
modifiers.Remove(modifier);
UpdateModifiedValue();
}
}
[Serializable]
public class Attribute_New
{
public AttributeType type;
public ModifiableInt_New value;
}
[CreateAssetMenu(fileName = "New Stats", menuName = "Stats System/New Character Stats New")[
public class StatsObject_New : ScriptableObject
{
public Attribute_New[] attributes;
public int level;
public int exp;
public int Health { get; set; } // Property로 만들면 Serializable 되지 않는다.
public int Mana { get; set; }
public float HealthPercentage {
get {
int health = Health;
int maxHealth = Health;
foreach (Attribute_New attribute in attributes) {
if (attribute.type == AttributeType.Health) {
maxHealth = attribute.value.ModifiedValue;
}
}
return (maxHealth > 0 ? ((float)health / (float)maxHealth) : 0f);
}
}
public float ManaPercentage {
get {
int mana = Mana;
int maxMana = Mana;
foreach (Attribute_New attribute in attributes) {
if (attribute.type == AttributeType.Mana) {
maxMana = attribute.value.ModifiedValue;
}
}
return (maxMana > 0 ? ((float)mana / (float)maxMana) : 0f);
}
}
public event Action<StatsObject> OnChangedStats;
[NonSerialized]
private bool isInitialize = false;
public void OnEnable() {
InitializeAttribute();
}
public void InitializeAttribute() {
if (isInitialize) return;
isInitialize = true;
foreach (Attribute_New attribute in attributes) {
attribute.value = new ModifiableInt_New(OnModifiedValue);
}
level = 1;
exp = 0;
SetBaseValue(AttributeType.Agility, 100);
SetBaseValue(AttributeType.Intellect, 100);
SetBaseValue(AttributeType.Stamina, 100);
SetBaseValue(AttributeType.Strength, 100);
SetBaseValue(AttributeType.Health, 100);
SetBaseValue(AttributeType.Mana, 100);
Health = GetModifiedValue(AttributeType.Health);
Mana = GetModifiedValue(AttributeType.Mana);
}
private void OnModifiedValue(ModifiableInt_New value) {
OnChangedStats?.Invoke(this);
}
public int GetBaseValue(AttributeType type) {
foreach (Attribute attribute in attributes) {
if (attribute.type == type) {
return attribute.value.BaseValue;
}
}
return -1;
}
public void SetBaseValue(AttributeType type, int value) {
foreach (Attribute attribute in attributes) {
if (attribute.type == type) {
attribute.value.BaseValue = value;
}
}
}
public int GetModifiedValue(AttributeType type) {
foreach (Attribute attribute in attributes) {
if (attribute.type == type) {
return attribute.value.ModifiedValue;
}
}
return -1;
}
public int AddHealth(int value) {
Health += value;
OnChangedStats?.Invoke(this);
return Health;
}
public int AddMana(int value) {
Mana += value;
OnChangedStats?.Invoke(this);
return Mana;
}
}
이제 Unity Editor에서 PlayerStats를 추가하여 위와 같이 설정해 줍니다.
위와 관련 기존 코드에서 수정되는 부분들만 작성합니다.
public class PlayerCharacter
{
[SerializeField]
public StatsObject playerStats;
public void OnEnterAttackState() {
UnityEngine.Debug.Log("OnEnterAttackState()");
playerStats.AddMana(-3); // 임시 테스트값
}
public void OnExitAttackState() {
UnityEngine.Debug.Log("OnExitAttackState()");
}
public bool IsAlive => playerStats.Health > 0;
public void TakeDamage(int damage, GameObject damageEffectPrefab) {
//..
playerStats.AddHealth(-damage);
//..
}
private void OnUseItem(ItemObject itemObject) {
foreach (ItemBuff buff in itemObject.data.buffs) {
if (buff.stat == AttributeType.Health) {
playerStats.AddHealth(buff.value);
}
}
}
}
Unity에서 플레이어의 속성 UI를 표시하기 위해 무료 Asset을 받아서 Canvas에 위와 같이 설정합니다.
public class PlayerInGameUI_New : MonoBehaviour
{
public StatsObject_New playerStats;
public Text levelText;
public Image healthSlider;
public Image manaSlider;
void Start() {
levelText.text = playerStats.level.ToString("n0");
healthSlider.fillAmount = playerStats.HealthPercentage;
manaSlider.fillAmount = playerStats.ManaPercentage;
}
private void OnEnable() {
playerStats.OnChangedStats += OnChangedStats;
}
private void OnDisable() {
playerStats.OnChangedStats -= OnChangedStats;
}
private void OnChangedStats(StatsObject_New statsObject) {
levelText.text = playerStats.level.ToString("n0");
healthSlider.fillAmount = playerStats.HealthPercentage;
manaSlider.fillAmount = playerStats.ManaPercentage;
}
}
플레이 화면입니다.
공격을 당하면 HP가 줄어들고, 공격을 하면 Mana가 줄어들고, 물약을 먹으면 HP가 다시 채워지는 것을 확인할 수 있습니다.
이제 위와 같은 속성을 표시하기 위한 구현입니다.
public class PlayerStatsUI_New : MonoBehaviour
{
public InventoryObject equipment;
public StatsObject playerStats;
public Text[] attributeTexts;
private void OnEnable() {
playerStats.OnChangedStats += OnChangedStats;
if (equipment != null && playerStats != null) {
foreach (InventorySlot slot in equipment.Slots) {
slot.OnPreUpdate += OnRemoveItem;
slot.OnPostUpdate += OnEquipItem;
}
UpdateAttributeTexts();
}
}
private void OnDisable() {
playerStats.OnChangedStats -= OnChangedStats;
if (equipment != null && playerStats != null) {
foreach (InventorySlot slot in equipment.Slots) {
slot.OnPreUpdate -= OnRemoveItem;
slot.OnPostUpdate -= OnEquipItem;
}
UpdateAttributeTexts();
}
}
private void UpdateAttributeTexts() {
attributeTexts[0].text = playerStats.GetModifiedValue(AttributeType.Agility).ToString("n0");
attributeTexts[1].text = playerStats.GetModifiedValue(AttributeType.Intellect).ToString("n0");
attributeTexts[2].text = playerStats.GetModifiedValue(AttributeType.Stamina).ToString("n0");
attributeTexts[3].text = playerStats.GetModifiedValue(AttributeType.Strength).ToString("n0");
}
private void OnRemoveItem(InventorySlot slot) {
if (slot.ItemObject == null) return;
foreach (ItemBuff buff in slot.item.buffs) {
foreach (Attribute attribute in playerStats.attributes) {
if (attribute.type == buff.stat) {
attribute.value.RemoveModifier(buff);
}
}
}
}
private void OnEquipItem(InventorySlot slot) {
if (slot.ItemObject == null) return;
foreach (ItemBuff buff in slot.item.buffs) {
foreach (Attribute attribute in playerStats.attributes) {
if (attribute.type == buff.stat) {
attribute.value.AddModifier(buff);
}
}
}
}
private void OnChangedStats(StatsObject statsObject) {
UpdateAttributeTexts();
}
}
여기까지 작성하고 플레이를 시작하여 장착 테스트를 해봅니다.
모든 값들이 잘 표시됨을 확인할 수 있습니다. @~@;;;;
<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>
패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0
'[컴퓨터] > 웹 | 앱 | 게임 개발' 카테고리의 다른 글
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 23회차 미션 (0) | 2020.11.10 |
---|---|
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 22회차 미션 (0) | 2020.11.09 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 20회차 미션 (0) | 2020.11.07 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 19회차 미션 (0) | 2020.11.06 |
[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 18회차 미션 (0) | 2020.11.05 |