ºÇ½ª¹¹¿·¡§ orika_ex_miyako 2024ǯ04·î15Æü(·î) 14:10:46ÍúÎò
°ìÄê»þ´Ö·Ð²á¤´¤È¤Ë»ØÄꤷ¤¿ÈÏ°ÏÆâ¤Ë¹¶·âÂоݤ¬¤¤¤ë¤³¤È¤ò³Îǧ¤·¡¢Âоݤ¬¤¤¤ë¾ì¹ç¤Ë¤Ï¡¢¤½¤Î¤¦¤Á¤ÎºÇ¤â¥×¥ì¥¤¥ä¡¼¤Ë¶á¤¤ÂоݤË1²ó¹¶·â¤ò¹Ô¤¤¤Þ¤¹¡£
¤³¤ì¤ò¼«Æ°Åª¤Ë·«¤êÊÖ¤·¤Þ¤¹¡£
¡¡
¡¡±þÍѤ¹¤ë¤³¤È¤Ç¥ô¥¡¥ó¥Ñ¥¤¥¢¥µ¥Ð¥¤¥Ð¡¼¥º¤ä¥¢¡¼¥Á¥ã¡¼ÅÁÀâ¤Ê¤É¤Î¤è¤¦¤Ê¥ª¡¼¥È¥Ð¥È¥ë¥·¥¹¥Æ¥à¤Îº¬´´¤È¤·¤ÆÍøÍѲÄǽ¤Ç¤¹¡£
¡¡¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹¤È´ðÄ쥯¥é¥¹¤òºîÀ®¤·¤¿¾å¡¢MVP ¥Ñ¥¿¡¼¥ó¤òºÎÍѤ·¡¢Model¡¢View¡¢Presenter ¤Î£³¤Ä¤òºîÀ®¤·¤Þ¤¹¡£
using UnityEngine; /// <summary> /// ½é´üÀßÄ궦Ä̲½ÍѤΥ¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹ /// </summary> public interface ISetup { void SetUp(GameObject entityObject = null); }
using UnityEngine; using UnityEngine.UI; using DG.Tweening; /// <summary> /// Slider ÍÑ´ðÄ쥯¥é¥¹ /// </summary> public class BarBase : MonoBehaviour { #pragma warning disable 0649 [SerializeField] protected CanvasGroup canvasGroup; // TODO ¤¢¤È¤Ç TextViewBase ¤È ViewBase ¤È¤·¤Æ¶¦Ä̲½¤¹¤ë [SerializeField] protected Canvas canvas; // ¤¢¤È¤Ç TextViewBase ¤È¶¦Ä̲½¤¹¤ë [SerializeField] protected Slider slider; #pragma warning restore 0649 /// <summary> /// ¥¹¥é¥¤¥À¡¼É½¼¨¹¹¿· /// </summary> /// <param name="currentValue"></param> public virtual void UpdateBar(float currentValue) { slider.DOValue(currentValue, 0.1f).SetEase(Ease.Linear).SetLink(gameObject); } /// <summary> /// ½é´üÀßÄê /// </summary> /// <param name="maxValue"></param> public virtual void SetUpView(float maxValue) { slider.maxValue = maxValue; if (canvas.worldCamera == null) { canvas.worldCamera = Camera.main; } } /// <summary> /// ¥¹¥é¥¤¥À¡¼É½¼¨ /// </summary> public virtual void ShowBarView() { //canvasGroupTextView.alpha = 1.0f; canvas.enabled = true; } /// <summary> /// ¥¹¥é¥¤¥À¡¼Èóɽ¼¨ /// </summary> public virtual void HideBarView() { //canvasGroupTextView.alpha = 0f; canvas.enabled = false; } public Transform GetCanvasTran() { return canvas.transform; } }
using UniRx; /// <summary> /// ¹¶·â½èÍý Model /// </summary> public class AttackModeModel { public ReactiveProperty<float> CurrentActionGaugePoint; private readonly CompositeDisposable disposables = new(); public float MaxActionGaugePoint { get; private set; } public float AttackSpeed { get; private set; } public int Atk { get; private set; } public int Inte { get; private set; } public int Def { get; private set; } public int Mdef { get; private set; } public float AttackRange { get; private set; } public DamageType DamageType { get; private set; } public eAttribute AttributeType { get; private set; } public int MinDmg { get; private set; } public AttackModeModel(float attackSpeed, int atk, float attackRange) { } /// <summary> /// ¥³¥ó¥¹¥È¥é¥¯¥¿ /// </summary> /// <param name="maxAttackPoint">ConstData ¤«¤é»²¾È(300)</param> /// <param name="spd">ConstaData ¤«¤é»²¾È(199) + spd</param> /// <param name="atk"></param> /// <param name="inte"></param> /// <param name="def"></param> /// <param name="mdef"></param> /// <param name="attackRange">¹¶·âÈÏ°Ï</param> /// <param name="damageType">¹¶·â¤Î¥¿¥¤¥×¡£ÊªÍý¤«ËâË¡</param> /// <param name="attributeType">¹¶·â¤Î°À</param> /// <param name="minDmg">ºÇ¾®¥À¥á¡¼¥¸ÃÍPlayer = 0</param> public AttackModeModel(float spd, int atk, int inte, int def, int mdef, float attackRange, DamageType damageType, eAttribute attributeType, int minDmg = 0) { CurrentActionGaugePoint = new(0f); MaxActionGaugePoint = ConstData.MAX_ACTION_GAUGE_POINT; AttackSpeed = ConstData.ATTACK_SPEED_OFFSET + spd; Atk = atk; Inte = inte; Def = def; Mdef = mdef; AttackRange = attackRange; DamageType = damageType; AttributeType = attributeType; MinDmg = minDmg; } /// <summary> /// ¥¢¥¯¥·¥ç¥ó¥²¡¼¥¸ÃͤΥꥻ¥Ã¥È /// </summary> public void ResetActionGaugePoint() { CurrentActionGaugePoint.Value = 0f; } /// <summary> /// ¥ª¥Ö¥¸¥§¥¯¥È(¥¤¥ó¥¹¥¿¥ó¥¹)¤¬ÇË´þ¤µ¤ì¤ë¤È¤¤ËCompositeDisposable¤ÎDispose¤â¸Æ¤Ó½Ð¤¹ /// </summary> public void Dispose() { // Dispose ¥á¥½¥Ã¥É¤¬¸Æ¤Ð¤ì¤¿¤È¤¤Ë¡¢CompositeDisposable ¤Ë´Þ¤Þ¤ì¤ëÁ´¤Æ¤Î¥¹¥È¥ê¡¼¥à¤¬¼«Æ°Åª¤Ë²òÊü disposables.Dispose(); } }
/// <summary> /// ¹¶·â½èÍý View /// </summary> public class ActionGuageBar : BarBase { /// <summary> /// ½é´üÀßÄê /// </summary> /// <param name="maxValue"></param> public override void SetUpView(float maxValue) { base.SetUpView(maxValue); // ½é´üÃͤò 0 ¤ËÀßÄê slider.value = 0; // ±£¤¹ HideBarView(); } /// <summary> /// ¹¶·â¥²¡¼¥¸¤Îɽ¼¨¹¹¿· /// </summary> /// <param name="currentValue"></param> public override void UpdateBar(float currentValue) { slider.value = currentValue; } }
using UnityEngine; using UniRx; using UniRx.Triggers; using System.Collections.Generic; using System.Linq; /// <summary> /// ¹¶·â½èÍý Presenter /// </summary> public class AttackModePresenter : ISetup { private LifeBase attackTarget; private ReactiveProperty<bool> IsAttackReady = new(false); private AttackModeModel myAttackModeModel; private ActionGuageBar attackModeView; private HpBar hpBarView; private AttackRangeCollider attackRangeCollider; /// <summary> /// ¥³¥ó¥¹¥È¥é¥¯¥¿ /// </summary> public AttackModePresenter() {} /// <summary> /// ¥³¥ó¥¹¥È¥é¥¯¥¿ /// </summary> /// <param name="spd"></param> /// <param name="attackPower"></param> /// <param name="attackRange"></param> public AttackModePresenter(float spd, int atk, int inte, int def, int mdef, float attackRange, DamageType damageType, eAttribute attributeType, int minDmg = 0) { myAttackModeModel = new(spd, atk, inte, def, mdef, attackRange, damageType, attributeType, minDmg); } /// <summary> /// ½é´üÀßÄê /// </summary> /// <param name="entityObject"></param> public void SetUp(GameObject entityObject) { // entityObject ¤«¤é¹¶·âÍѤΥǡ¼¥¿¤ò»²¾È¤·¤Æ¤â¤é¤¦ if (entityObject.TryGetComponent(out attackModeView)) { attackModeView.SetUpView(ConstData.MAX_ACTION_GAUGE_POINT); } // Hp ¼èÆÀ LifeBase myLife; if (!entityObject.TryGetComponent(out myLife)) { return; } myAttackModeModel = myLife.AttackModeModel; // HpBar ¼èÆÀ if (entityObject.TryGetComponent(out hpBarView)) { hpBarView.SetUpView(myLife.Hp.Value); } // Hp ¤Î¹ØÆÉ Hp ¥²¡¼¥¸¹¹¿· myLife.Hp.Subscribe(hp => hpBarView.UpdateBar(myLife.Hp.Value)).AddTo(entityObject); // ¹¶·âÈÏ°ÏÍѤΥ³¥é¥¤¥À¡¼¤Î¼èÆÀ¤¬¤Ç¤¤Ê¤¤¾ì¹ç¡¢½èÍý¤·¤Ê¤¤ if (!entityObject.TryGetComponent(out attackRangeCollider)) { return; } // ¹¶·âÈϰϤΥ³¥é¥¤¥À¡¼¤Î¥µ¥¤¥º¤òÀßÄê attackRangeCollider.SetUpColliderRange(myAttackModeModel.AttackRange); // ¥â¥Ã¥¯ÍÑ if (myLife.EntityType == EntityType.Enemy) { return; } // ¹¶·âÂоݤȤʤ륿¥°¤òÀßÄê string tag = myLife.GetTargetEntityType().ToString(); // ¹¶·âÂоݤÎõº÷ entityObject.UpdateAsObservable() .Subscribe(_ => SearchAttackTarget(entityObject, tag)); // ¿¯ÆþȽÄê¤Î¹ØÆÉ ¼«Æ°¹¶·â½àÈ÷ //attackRangeCollider.AttackRange.OnTriggerStay2DAsObservable() // .Where(col => attackTarget == null) // .Where(col => col.transform.TryGetComponent(out attackTarget)) // ¥³¥é¥¤¥À¡¼¤Ø¤Î¿¯ÆþȽÄê // .Where(_ => myLife.CheckTarget(attackTarget.EntityType)) // ¹¶·âÂоݤ«È½Äê // .Subscribe(col => { // // ¹¶·â½àÈ÷³«»Ï¤È¥²¡¼¥¸É½¼¨ // IsAttackReady.Value = true; // attackModeView.ShowBarView(); // Debug.Log($"¹¶·â½àÈ÷ ³«»Ï {attackTarget}"); // }); // ¤¤¤Ê¤¯¤Ê¤Ã¤¿È½Äê¤Î¹ØÆÉ ¼«Æ°¹¶·âÄä»ß //attackRangeCollider.AttackRange.OnTriggerExit2DAsObservable() // .Where(col => col.transform.TryGetComponent(out attackTarget)) // ¥³¥é¥¤¥À¡¼¤Ø¤Î¿¯ÆþȽÄê // .Where(_ => myLife.CheckTarget(attackTarget.EntityType)) // ¹¶·âÂоݤ«È½Äê // .Subscribe(col => StopAttack()); // Update ¤Î¹ØÆÉ entityObject.UpdateAsObservable() .Where(_ => IsAttackReady.Value) .Where(_ => myAttackModeModel.CurrentActionGaugePoint.Value < myAttackModeModel.MaxActionGaugePoint) .Subscribe(_ => { // ¹¶·â¥²¡¼¥¸¤Î²Ã»» myAttackModeModel.CurrentActionGaugePoint.Value += myAttackModeModel.AttackSpeed * Time.deltaTime; // ¹¶·â¥²¡¼¥¸¤Î View ¹¹¿· attackModeView.UpdateBar(myAttackModeModel.CurrentActionGaugePoint.Value); }); // ¹¶·â¥²¡¼¥¸¤Î¹ØÆÉ myAttackModeModel.CurrentActionGaugePoint .Where(_ => attackTarget) .Where(currentActionGaugePoint => currentActionGaugePoint >= myAttackModeModel.MaxActionGaugePoint) .Subscribe(_ => { if (attackTarget == null) { // ¥¢¥¯¥·¥ç¥ó¥²¡¼¥¸¥ê¥»¥Ã¥È myAttackModeModel.ResetActionGaugePoint(); } else { // ¼«Æ°¹¶·â AutoAttack(); } }) .AddTo(entityObject); // Ç˲õ»þ¤Î¹ØÆÉ entityObject.OnDestroyAsObservable().Subscribe(_ => myAttackModeModel.Dispose()); // TODO ̾Á°É½¼¨ //Debug.Log($"{this} Setup ´°Î»"); } /// <summary> /// ¼«Æ°¹¶·â /// </summary> private void AutoAttack() { //Debug.Log("¼«Æ°¹¶·â"); // °À·¸¿ô»»½Ð float attributeRate = BattleCalculater.CalcAttributeRate(myAttackModeModel.AttributeType, attackTarget.AttackModeModel.AttributeType); //Debug.Log($"°ÀÊäÀµ : {attributeRate}"); //Debug.Log($"¹¶·âÎÏ : {myAttackModeModel.Atk}"); //Debug.Log($"ÃÎÎÏ : {myAttackModeModel.Inte}"); //Debug.Log($"ËɸæÎÏ : {attackTarget.AttackModeModel.Def}"); // ¸½ºß¤Î¹¶·â¥¿¥¤¥×¤ò¸µ¤Ë¥À¥á¡¼¥¸·×»» int damage = myAttackModeModel.DamageType == DamageType.atk ? BattleCalculater.CalcAtkDamage(myAttackModeModel.Atk, myAttackModeModel.Inte, attributeRate, attackTarget.AttackModeModel.Def) // ʪÍý¥À¥á¡¼¥¸ : BattleCalculater.CalcInteDamage(myAttackModeModel.Inte, myAttackModeModel.Atk, attributeRate, attackTarget.AttackModeModel.Mdef); // ËâË¡¥À¥á¡¼¥¸ Debug.Log($"ºÇ½ª¥À¥á¡¼¥¸ : {damage}"); // ¥¢¥¯¥·¥ç¥ó¥²¡¼¥¸¥ê¥»¥Ã¥È myAttackModeModel.ResetActionGaugePoint(); // ¹¶·âÂоݤ¬»Ä¤Ã¤Æ¤¤¤ë¤«³Îǧ if (attackTarget == null) { Debug.Log("¹¶·âÂоݾüº"); return; } // ¥À¥á¡¼¥¸Ãͤò¥Õ¥í¡¼¥Èɽ¼¨¡£¥ª¥Ö¥¸¥§¥¯¥È¥×¡¼¥ëÍøÍÑ attackTarget.ShowFloatingView(damage); //FloatingView floatingView = (FloatingView)FloatingViewGenerator.instance.GetObjectFromPool(attackTarget.transform.position, attackTarget.transform.rotation); //floatingView.SetUpView(damage.ToString()); // Hp ·×»»¡£ÂоݤΠ»Ä hp ¤¬ 0 °Ê²¼¤Ê¤é if (!attackTarget.CalcLife(-damage)) { // ¼«Æ°¹¶·âÄä»ß StopAttack(); } //Debug.Log("¹¶·â´°Î»"); } /// <summary> /// ¼«Æ°¹¶·âÄä»ß /// </summary> private void StopAttack() { attackTarget = null; // ¹¶·âÄä»ß¡£¥¢¥¯¥·¥ç¥ó¥²¡¼¥¸¥ê¥»¥Ã¥È¤È¥²¡¼¥¸Èóɽ¼¨ IsAttackReady.Value = false; myAttackModeModel.ResetActionGaugePoint(); attackModeView.HideBarView(); //Debug.Log("¹¶·âÄä»ß"); } /// <summary> /// ¹¶·âÎϤʤɤΥǡ¼¥¿¹¹¿· /// </summary> /// <param name="attackModeModel"></param> public void UpdateAttackModel(AttackModeModel attackModeModel, GameObject entityObject) { myAttackModeModel = attackModeModel; myAttackModeModel.Dispose(); // ¹¶·â¥²¡¼¥¸¤Î¹ØÆɤä¤êľ¤· myAttackModeModel.CurrentActionGaugePoint .Where(_ => attackTarget) .Where(currentActionGaugePoint => currentActionGaugePoint >= myAttackModeModel.MaxActionGaugePoint) .Subscribe(_ => AutoAttack()) .AddTo(entityObject); Debug.Log("¹¹¿·"); } /// <summary> /// ¹¶·âÂоݤÎõº÷ /// </summary> /// <param name="entityObject"></param> /// <param name="tag"></param> private void SearchAttackTarget(GameObject entityObject, string tag) { // OverlapCircleAll ¥á¥½¥Ã¥É¤òÍøÍѤ·¤Æ¡¢ÈÏ°ÏÆâ¤Î¥³¥é¥¤¥À¡¼ÉÕ¤¤Î¥ª¥Ö¥¸¥§¥¯¥È¤ò¼èÆÀ //Collider2D[] hitColliders = Physics2D.OverlapCircleAll(entityObject.transform.position, myAttackModeModel.AttackRange / 2); //List<GameObject> objList = new(); //// ÇÛÎóÆâ¤ÎÍ×ÁǤò£±¤Ä¤º¤Ä¼è¤ê½Ð¤¹ //foreach (Collider2D collider in hitColliders) { // // »ØÄꤵ¤ì¤¿¥¿¥°¤ò»ý¤Ä¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ë¤«È½Äê¡£¤½¤ì¤ò¹¶·âÂоݸõÊä¤È¤·¤Æǧ¼± // if (collider.gameObject.CompareTag(tag)) { // objList.Add(collider.gameObject); // } //} // ¾åµ¤ò LINQ ¤Çµ½Ò List<GameObject> objList = Physics2D.OverlapCircleAll(entityObject.transform.position, myAttackModeModel.AttackRange / 2) .Where(collider => collider.CompareTag(tag)) .Select(collider => collider.gameObject) .ToList(); // ¼èÆÀ¤·¤¿¥²¡¼¥à¥ª¥Ö¥¸¥§¥¯¥È¤¬ 0 ¤Ê¤é¹¶·âÂоݤθõÊä¤Ê¤· if (objList.Count == 0) { Debug.Log("¹¶·âÂоݤʤ·"); StopAttack(); return; } // ºÇ¤â¶á¤¤¥ª¥Ö¥¸¥§¥¯¥È¤Îµ÷Î¥¤òÂåÆþ¤¹¤ë¤¿¤á¤ÎÊÑ¿ô //float nearDistance = 0; // ¸¡º÷¤µ¤ì¤¿ºÇ¤â¶á¤¤¥²¡¼¥à¥ª¥Ö¥¸¥§¥¯¥È¤òÂåÆþ¤¹¤ë¤¿¤á¤ÎÊÑ¿ô //GameObject searchTargetObj = null; // objs¤«¤é£±¤Ä¤º¤ÄobjÊÑ¿ô¤Ë¼è¤ê½Ð¤¹ //foreach (GameObject obj in objList) { // // obj¤Ë¼è¤ê½Ð¤·¤¿¥²¡¼¥à¥ª¥Ö¥¸¥§¥¯¥È¤È¡¢¤³¤Î¥²¡¼¥à¥ª¥Ö¥¸¥§¥¯¥È¤È¤Îµ÷Î¥¤ò·×»»¤·¤Æ¼èÆÀ // float distance = Vector3.Distance(obj.transform.position, entityObject.transform.transform.position); // // nearDistance¤¬0(ºÇ½é¤Ï¤³¤Á¤é)¡¢¤¢¤ë¤¤¤ÏnearDistance¤¬distance¤è¤ê¤âÂ礤¤Ãͤʤé // if (nearDistance == 0 || nearDistance > distance) { // // nearDistance¤ò¹¹¿· // nearDistance = distance; // // searchTargetObj¤ò¹¹¿· // searchTargetObj = obj; // } //} // ¾åµ¤ò LINQ ¤Çµ½Ò¡£ºÇ¤â¶á¤¤ÂоݤòÃê½Ð GameObject searchTargetObj = objList .OrderBy(obj => Vector3.Distance(obj.transform.position, entityObject.transform.position)) .FirstOrDefault(); //ºÇ¤â¶á¤«¤Ã¤¿¥ª¥Ö¥¸¥§¥¯¥È¤ò¹¶·âÂоݤȤ¹¤ë attackTarget = searchTargetObj.GetComponent<LifeBase>(); // ¹¶·â½àÈ÷³«»Ï¤È¥²¡¼¥¸É½¼¨ IsAttackReady.Value = true; attackModeView.ShowBarView(); Debug.Log($"¹¶·â½àÈ÷ ³«»Ï {attackTarget}"); } }
¡¡»ØÄꤷ¤¿ÈÏ°ÏÆâ¤Ë¥³¥é¥¤¥À¡¼¤òŸ³«¤·¡¢¤½¤ÎÈÏ°ÏÆâ¤Ë¤¢¤ë¥³¥é¥¤¥À¡¼¤ò¼èÆÀ¤·¤ÆÇÛÎó¤ËÂåÆþ¤¹¤ë¥á¥½¥Ã¥É¤Ç¤¹¡£
¡¡¥ì¥¤¥ä¡¼¤Ç»ØÄꤷ¡¢GC ¤ò¾¯¤Ê¤¯¤¹¤ë OverlapCircleNonAlloc ¥á¥½¥Ã¥É¤â¤¢¤ê¤Þ¤¹¡£
https://docs.unity3d.com/ja/current/ScriptReferenc...
https://docs.unity3d.com/ja/current/ScriptReferenc...
- ¥«¥Æ¥´¥ê¡§
- ¿Ê³Ø/¥¹¥¯¡¼¥ë
- ¥×¥í¥°¥é¥ß¥ó¥°
¤³¤Î¥Ú¡¼¥¸¤Ø¤Î¥³¥á¥ó¥È
UniRX¤ò»È¤¦¤È¤É¤ó¤Ê¤È¤³¤í¤¬¤¤¤¤¤Ç¤¹¤«¡©
»ä¤Ï´Ú¹ñ¿Í¤Ç¡¢¸½ºß2D Action RPG¤òºî¤Ã¤Æ¤¤¤Þ¤¹¡£
¾ðÊó¤òõ¤·¤Æ¤¤¤ëÅÓÃæ¡¢»ä¤¬Ë¾¤à¾ðÊó¤È»÷¤¿¾ðÊó¤ò¸«¤Ä¤±¤¿¤è¤¦¤Ê¤Î¤Ç¡¢¤ªÊ¹¤¤·¤¿¤¤¤Î¤Ç¤¹¤¬¡¢UniRX¤Ë¤Ï¤É¤Î¤è¤¦¤Ê¥á¥ê¥Ã¥È¤¬¤¢¤ë¤Î¤Ç¤·¤ç¤¦¤«¡©
ÉÔËܰդʤ¬¤é¤ª¼ê¿ô¤ò¤ª¤«¤±¤·¤Æ¿½¤·Ìõ¤¢¤ê¤Þ¤»¤ó¡£