2
1

More than 3 years have passed since last update.

[Unity3D] 武器の実装

Posted at

1, Playerプレハブの手の部分にEquippedSwordを作成する
953BA4C6-8D1E-48D6-9318-7026A0CD5A48.jpeg

2, EquippedSwordに武器を子オブジェクト化し、再生した状態でScaleや位置を調整してコピーしておく
CCF6A698-8908-4034-8FEC-A044B886FA5C.jpeg
D2B6756D-7A0E-4258-898B-3081095E0514_4_5005_c.jpeg

3, コピーした位置をPlayerのTransformにPasteする
92AD8B33-2689-4267-907D-DAEF02C77560.jpeg

4, Waeponsフォルダを作成し、プレハブ化する。(プレハブ済のものは削除しておく)
810B8AEE-BB9B-43AF-81F0-97813A12D7EF.jpeg
06EF6F8E-5F93-4CA0-973C-D65FB9EFDAC7.jpeg

5, Fighterに下記コードを追加する

Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;
using System;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float weaponRange = 2f;
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] GameObject weaponPrefab = null; //追加
        [SerializeField] Transform handTransform = null; //追加

        Health target;
        float timeSinceLastAttack = Mathf.Infinity;

        //追加
        private void Start()
        {
            SpawnWeapon();
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        //追加
        private void SpawnWeapon()
        {
            Instantiate(weaponPrefab, handTransform);
        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }
            target.TakeDamage(weaponDamage);
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < weaponRange;
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

6, コンポーネントに位置と武器を設定する
0A94B483-F26F-49E5-947C-8E273581303E.jpeg

アニメーションの実装

1, Swordフォルダを作成し、EquippedSwordを入れる
A2E0BE18-0241-47F5-88DB-3F3AE14804B3_4_5005_c.jpeg

2, SwordフォルダにAnimatorOverrideControllerを作成する
913BB3F2-C216-4294-B8C5-0CEDF111A8CC_4_5005_c.jpeg

3, SwordOverrideControllerに名前を変更し、CharactorControllerを設定する
34C31F37-7965-4F0D-B740-26E4F48D9CF1.jpeg

4, SwordOverrideControllerのUnarmed-Attakに任意のアニメーションを設定する
50E55896-6730-4A28-A68C-9D82A8B994B5_4_5005_c.jpeg

5, Fighterに下記スクリプトを書く

Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;
using System;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float weaponRange = 2f;
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] GameObject weaponPrefab = null;
        [SerializeField] Transform handTransform = null;
        [SerializeField] AnimatorOverrideController weaponOverride = null; //追加

        Health target;
        float timeSinceLastAttack = Mathf.Infinity;

        private void Start()
        {
            SpawnWeapon();
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        private void SpawnWeapon()
        {
            Instantiate(weaponPrefab, handTransform);
            Animator animator = GetComponent<Animator>(); //追加
            animator.runtimeAnimatorController = weaponOverride; //追加

        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }
            target.TakeDamage(weaponDamage);
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < weaponRange;
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

6,PlayerプレハブのWeaponOverrideにSwordOverrideControllerをアタッチする
2CA40A2C-2417-42D0-ABCA-B296B63D85D1.jpeg

スクリプト化可能なオブジェクトの作成

1, Wwaponスクリプトを作成し、各スクリプトに下記コードを書く
83962986-D6BB-4A76-A1F1-C61A6A462EC0_4_5005_c.jpeg

Weapon
using UnityEngine;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Weapon", menuName = "Weapons/Make New Weapon", order = 0)]
    public class Weapon : ScriptableObject
    {
        [SerializeField] AnimatorOverrideController animatorOverride = null;
        [SerializeField] GameObject weaponPrefab = null;

        public void Spawn(Transform handTransform, Animator animator)
        {
            Instantiate(weaponPrefab, handTransform);
            animator.runtimeAnimatorController = animatorOverride;
        }
    }
}
Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;
using System;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float weaponRange = 2f;
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] Transform handTransform = null;
        [SerializeField] Weapon weapon = null; //追加

        Health target;
        float timeSinceLastAttack = Mathf.Infinity;

        private void Start()
        {
            SpawnWeapon();
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        private void SpawnWeapon()
        {
            if (weapon == null) return; //追加
            Animator animator = GetComponent<Animator>();
            weapon.Spawn(handTransform, animator); //追加

        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }
            target.TakeDamage(weaponDamage);
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < weaponRange;
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

2, Swordフォルダ内にCreate→Weapons→MakeNewWeaponsを押してSwordを作成する
45CFF818-24C8-4B9C-8B12-354B4D28C379.jpeg
57843597-903B-4C35-AF88-2FBA27EF8AA0_4_5005_c.jpeg

3, SwordにSwordOverrideControllerとEquippedSwordを設定する
DA3FE0A1-BEAF-4636-A6C4-FE261C98DACB_4_5005_c.jpeg

4, PlayerプレハブのWeaponにSwordをアタッチする
42AA4536-B0B8-411A-877F-60199DFB9929.jpeg

武器無し攻撃の実装

1, Weaponsフォルダ内にUnarmedフォルダを作成し、Create→Weapons→MakeNewWeaponでUnarmedを作成。
822E6AC4-1508-4157-AE64-384DB753A84A_4_5005_c.jpeg
CEED2AFC-60C7-4459-8E8F-4317CF892897_4_5005_c.jpeg

2, 各スクリプトに下記コードを書く

Weapon
using UnityEngine;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Weapon", menuName = "Weapons/Make New Weapon", order = 0)]
    public class Weapon : ScriptableObject
    {
        [SerializeField] AnimatorOverrideController animatorOverride = null;
        [SerializeField] GameObject equippedPrefab = null;
        [SerializeField] float weaponDamage = 5f; //Fighter.csから移動
        [SerializeField] float weaponRange = 2f; //Fighter.csから移動

        public void Spawn(Transform handTransform, Animator animator)
        {
            if (equippedPrefab != null)
            {
                Instantiate(equippedPrefab, handTransform);
            }
            if (animatorOverride != null)
            {
                animator.runtimeAnimatorController = animatorOverride;
            }
        }

        public float GetDamage()
        {
            return weaponDamage;
        }

        public float GetRange()
        {
            return weaponRange;
        }
    }
}
Fighter
void Hit()
        {
            if(target == null) { return; }
            target.TakeDamage(weapon.GetDamage()); //GetDamageに変更
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < weapon.GetRange(); //GetRangeに変更
        }

3, EnemyのWeaponにUnarmedを設定する
90344D3C-579C-4DCA-AA96-FB258486C3BF_4_5005_c.jpeg

4, SWordのEquippedPrefabにEquippedSwordオブジェクトを設定する
B8D4372F-F02B-41B7-BBD0-7938C6729B58.jpeg

武器を拾う

1, プロジェクトにPickupsフォルダを作成。ヒエラルキーにPickupsとSwordPickupのげーむオブジェクトを作成し、SwordPickupをプレハブ化する。
9F5502DD-E866-46AE-8156-806D07F92805.jpeg

2, SwordPickupに武器を入れ、サイズを調整する
3D1EF594-330D-4753-9BFA-0855F95BD767_4_5005_c.jpeg

3, SwordPickupにSphereColliderを付け、Colliderのサイズを調整する
D9D28624-7638-40B5-B326-6027CC5FD6E7.jpeg

4, WeaponPickupスクリプトを作成し、SwordPickupに付ける
3269E4AE-464A-44E3-8650-EA37E925B9BC.jpeg

5, 下記スクリプトを記述する

WeaponPickup
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Combat
{
    public class WeaponPickup : MonoBehaviour
    {
        [SerializeField] Weapon weapon = null;

        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.tag == "Player")
            {
                other.GetComponent<Fighter>().EquipWeapon(weapon);
                Destroy(gameObject);
            }
        }
    }
}
Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;
using System;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] Transform handTransform = null;
        [SerializeField] Weapon defaultWeapon = null; //変更

        Health target;
        float timeSinceLastAttack = Mathf.Infinity;
        Weapon currentWeapon = null; //追加

        private void Start()
        {
            EquipWeapon(defaultWeapon); //追加
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        public void EquipWeapon(Weapon weapon) //EquipWeaponに変更
        {
            currentWeapon = weapon; //追加
            Animator animator = GetComponent<Animator>();
            weapon.Spawn(handTransform, animator);

        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }
            target.TakeDamage(currentWeapon.GetDamage()); //currentWeaponに変更
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < currentWeapon.GetRange(); //currentWeaponに変更
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

6, SwordPickupのWeaponにSwordをアタッチし、IsTriggerにチェックを入れる
E403B5C2-7AB9-46B8-B8B6-F623B98D4907.jpeg

7, PlayerプレハブのFighterスクリプト→DefaultWeaponにUnarmedをアタッチする
1A35607D-BE22-49BB-91EA-65FD7A549F63.jpeg

8, CharactorにRigidbodyを付ける
A40129D6-6761-4486-9A23-CF3EF7B54FEB.jpeg

上記で再生したところplayerがプルプルしていたので追加でIsKinematicにチェックを入れました。
E9D97FFB-8FDA-41A8-A849-6C8B41AF225C_4_5005_c.jpeg

左手に持つ武器を実装

1, 武器のアセットをインポートする
536D1B0F-8A26-43C3-B268-1B42DAA4526A_4_5005_c.jpeg

2, 各スクリプトに下記コードを記述する

Weapon
using UnityEngine;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Weapon", menuName = "Weapons/Make New Weapon", order = 0)]
    public class Weapon : ScriptableObject
    {
        [SerializeField] AnimatorOverrideController animatorOverride = null;
        [SerializeField] GameObject equippedPrefab = null;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] float weaponRange = 2f;
        [SerializeField] bool isRightHanded = true; //追加


        public void Spawn(Transform rightHand, Transform leftHand, Animator animator) //追加
        {
            if (equippedPrefab != null)
            {
                Transform handTransform; //追加
                if (isRightHanded) handTransform = rightHand; //追加
                else handTransform = leftHand; //追加
                Instantiate(equippedPrefab, handTransform);
            }
            if (animatorOverride != null)
            {
                animator.runtimeAnimatorController = animatorOverride;
            }
        }

        public float GetDamage()
        {
            return weaponDamage;
        }

        public float GetRange()
        {
            return weaponRange;
        }
    }
}
Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;
using System;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] Transform rightHandTransform = null; //変更
        [SerializeField] Transform leftHandTransform = null; //追加
        [SerializeField] Weapon defaultWeapon = null;

        Health target;
        float timeSinceLastAttack = Mathf.Infinity;
        Weapon currentWeapon = null;

        private void Start()
        {
            EquipWeapon(defaultWeapon);
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        public void EquipWeapon(Weapon weapon)
        {
            currentWeapon = weapon;
            Animator animator = GetComponent<Animator>();
            weapon.Spawn(rightHandTransform, leftHandTransform, animator); //追加

        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }
            target.TakeDamage(currentWeapon.GetDamage());
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < currentWeapon.GetRange();
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}

3, PlayerのHandLとHandRをFighterコンポーネントにアタッチする
11BF02EC-3CF7-46B9-B1DC-712E277390C6.jpeg

4, SwordのIsRightHandedのチェックを外す
6D59AFD6-068F-426F-A708-856DF55E8490.jpeg

5, Bowフォルダを作成する
AE79B4B2-C834-451D-ABFF-1BD931736241_4_5005_c.jpeg

6, BowフォルダにMakeNewWeaponとAnimatorOverrideControllerを作成する
3EB0CD4B-F5CD-49DD-82C5-8544D97B03FB.jpeg
77DFC5C6-B94D-42C3-A769-EA4F2807AD30_4_5005_c.jpeg
F734E82E-B6EE-4928-A500-763C45F9159E_4_5005_c.jpeg

7,BowAnimatorOverrideにCharactorとアニメーションをアタッチする
0FB55E9B-EC5E-43B4-8C57-BC716A6F0584.jpeg

8, BowのAnimatorOverrideとdamage値など設定する
CAE7DD5A-586C-4C41-AC11-37FF0822F429.jpeg

9, PlayerのHandLにBowオブジェクトを作成し、武器を子オブジェクト化しておく。それをプレハブ化する
BA2D43D3-9F10-4845-832A-60F7F0081AD1.jpeg

10,再生しながら武器の位置を合わせてTransformをコピーする
7CF9420D-8115-4E5F-BC9E-2DDB90FE60AE.jpeg

11, 位置をPasteする
8ECFA1F9-60BC-4A74-B5C1-AA8F0BC8AD4F_4_5005_c.jpeg

12, PlayerのdefaltWeaponをBowにする
89ADF208-F6F3-45CF-9B8B-A8D0AC1A2942.jpeg

13, BowのEquippedPrefabを設定する
E9212927-1EE9-4932-9567-BC72C1AD9EC4.jpeg

矢をplayerに当てる

1, 矢のアセットをインポートする
35BA28DF-BD6F-44D6-9B66-F6F58460350C_4_5005_c.jpeg

2, ProjectにProjectileフォルダを作成。ヒエラルキーにArrowProjectileオブジェクトを作成し、プレハブ化する
218CBB19-FA4F-461F-8341-A93D4A6F4BE7.jpeg

3, ArrowProjectileに矢のプレハブを子オブジェクト化する
22D94C3E-E623-4D0A-9063-D29553CF8A67_4_5005_c.jpeg

4, SimpleArrowのサイズや向きを調整する
1584B30A-E612-4431-8F7D-54BE5C8EDDE8_4_5005_c.jpeg

5, Projectile.csを作成する
11EF0310-9EEA-4E13-BB8C-2361DB6D509C_4_5005_c.jpeg

6, 下記のコードを記述する

Projectile
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Projectile : MonoBehaviour
{
    [SerializeField] Transform target = null;
    [SerializeField] float speed = 1;

    void Update()
    {
        if (target == null) return;

        transform.LookAt(GetAimLocation());
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }

    private Vector3 GetAimLocation()
    {
        CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
        if (targetCapsule == null)
        {
            return target.position;
        }
        return target.position + Vector3.up * targetCapsule.height / 2;
    }
}

7, ArrowProjectileのTargetにplayerをアタッチする
E5684329-CB83-4613-9DD8-1199B720E30A.jpeg

矢を敵に飛ばす

1, アタックアニメーションを確認し、Hitなどのイベントがついているか確認する
70BCFB93-B7FF-473B-8DB7-96580A6B080E.jpeg

2, 各スクリプトに下記コードを追加する

Fighter
using UnityEngine;
using RPG.Movement;
using RPG.Core;
using System;

namespace RPG.Combat
{
    public class Fighter : MonoBehaviour, IAction
    {
        [SerializeField] float timeBetweenAttacks = 1f;
        [SerializeField] Transform rightHandTransform = null; //変更
        [SerializeField] Transform leftHandTransform = null; //追加
        [SerializeField] Weapon defaultWeapon = null;

        Health target;
        float timeSinceLastAttack = Mathf.Infinity;
        Weapon currentWeapon = null;

        private void Start()
        {
            EquipWeapon(defaultWeapon);
        }

        private void Update()
        {
            timeSinceLastAttack += Time.deltaTime;

            if (target == null) return;
            if (target.IsDead()) return;

            if (!GetIsInRange())
            {
                GetComponent<Mover>().MoveTo(target.transform.position, 1f);
            }
            else
            {
                GetComponent<Mover>().Cancel();
                AttackBehaviour();
            }
        }

        public void EquipWeapon(Weapon weapon)
        {
            currentWeapon = weapon;
            Animator animator = GetComponent<Animator>();
            weapon.Spawn(rightHandTransform, leftHandTransform, animator); //追加

        }

        private void AttackBehaviour()
        {
            transform.LookAt(target.transform);
            if (timeSinceLastAttack > timeBetweenAttacks)
            {
                TriggerAttack();
                timeSinceLastAttack = 0;
            }
        }

        private void TriggerAttack()
        {
            GetComponent<Animator>().ResetTrigger("stopAttack");
            GetComponent<Animator>().SetTrigger("attack");
        }

        void Hit()
        {
            if(target == null) { return; }

            if (currentWeapon.HasProjectile())
            {
                currentWeapon.LaunchProjectile(rightHandTransform, leftHandTransform, target);
            }
            else
            {
                target.TakeDamage(currentWeapon.GetDamage());
            }
        }

        void Shoot()
        {
            Hit();
        }

        private bool GetIsInRange()
        {
            return Vector3.Distance(transform.position, target.transform.position) < currentWeapon.GetRange();
        }

        public bool CanAttack(GameObject combatTarget)
        {
            if (combatTarget == null) { return false; }
            Health targetToTest = combatTarget.GetComponent<Health>();
            return targetToTest != null && !targetToTest.IsDead();
        }

        public void Attack(GameObject combatTarget)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            target = combatTarget.GetComponent<Health>();
        }

        public void Cancel()
        {
            StopAttack();
            target = null;
            GetComponent<Mover>().Cancel();
        }

        private void StopAttack()
        {
            GetComponent<Animator>().ResetTrigger("attack");
            GetComponent<Animator>().SetTrigger("stopAttack");
        }
    }
}
Weapon
using RPG.Core;
using UnityEngine;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Weapon", menuName = "Weapons/Make New Weapon", order = 0)]
    public class Weapon : ScriptableObject
    {
        [SerializeField] AnimatorOverrideController animatorOverride = null;
        [SerializeField] GameObject equippedPrefab = null;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] float weaponRange = 2f;
        [SerializeField] bool isRightHanded = true;
        [SerializeField] Projectile projectile = null;


        public void Spawn(Transform rightHand, Transform leftHand, Animator animator)
        {
            if (equippedPrefab != null)
            {
                Transform handTransform = GetTransform(rightHand, leftHand);
                Instantiate(equippedPrefab, handTransform);
            }
            if (animatorOverride != null)
            {
                animator.runtimeAnimatorController = animatorOverride;
            }
        }

        private Transform GetTransform(Transform rightHand, Transform leftHand)
        {
            Transform handTransform;
            if (isRightHanded) handTransform = rightHand;
            else handTransform = leftHand;
            return handTransform;
        }

        public bool HasProjectile()
        {
            return projectile != null;
        }

        public void LaunchProjectile(Transform rightHand, Transform leftHand, Health target)
        {
            Projectile projectileInstance = Instantiate(projectile, GetTransform(rightHand, leftHand).position, Quaternion.identity);
            projectileInstance.SetTarget(target);
        }

        public float GetDamage()
        {
            return weaponDamage;
        }

        public float GetRange()
        {
            return weaponRange;
        }
    }
}
Projectile
using System.Collections;
using System.Collections.Generic;
using RPG.Core;
using UnityEngine;

public class Projectile : MonoBehaviour
{
    [SerializeField] float speed = 1;

    Health target = null;

    void Update()
    {
        if (target == null) return;

        transform.LookAt(GetAimLocation());
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }

    public void SetTarget(Health target)
    {
        this.target = target;
    }

    private Vector3 GetAimLocation()
    {
        CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
        if (targetCapsule == null)
        {
            return target.transform.position;
        }
        return target.transform.position + Vector3.up * targetCapsule.height / 2;
    }
}

3, AxeのProjectileにArrowProjectileをアタッチする
55E99FD5-EC77-4FCF-82A8-840169611749.jpeg

ダメージの実装

1, 各スクリプトに下記のコードを書く

Projectile
using System.Collections;
using System.Collections.Generic;
using RPG.Core;
using UnityEngine;

public class Projectile : MonoBehaviour
{
    [SerializeField] float speed = 1;
    Health target = null;
    float damage = 0; //追加

    void Update()
    {
        if (target == null) return;

        transform.LookAt(GetAimLocation());
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }

    public void SetTarget(Health target, float damage) //danageを追加
    {
        this.target = target;
        this.damage = damage; //追加
    }

    private Vector3 GetAimLocation()
    {
        CapsuleCollider targetCapsule = target.GetComponent<CapsuleCollider>();
        if (targetCapsule == null)
        {
            return target.transform.position;
        }
        return target.transform.position + Vector3.up * targetCapsule.height / 2;
    }

    //追加
    private void OnTriggerEnter(Collider other)
    {
        if (other.GetComponent<Health>() != target) return;
        target.TakeDamage(damage);
        Destroy(gameObject);
    }
}
Weapon
using RPG.Core;
using UnityEngine;

namespace RPG.Combat
{
    [CreateAssetMenu(fileName = "Weapon", menuName = "Weapons/Make New Weapon", order = 0)]
    public class Weapon : ScriptableObject
    {
        [SerializeField] AnimatorOverrideController animatorOverride = null;
        [SerializeField] GameObject equippedPrefab = null;
        [SerializeField] float weaponDamage = 5f;
        [SerializeField] float weaponRange = 2f;
        [SerializeField] bool isRightHanded = true;
        [SerializeField] Projectile projectile = null;


        public void Spawn(Transform rightHand, Transform leftHand, Animator animator)
        {
            if (equippedPrefab != null)
            {
                Transform handTransform = GetTransform(rightHand, leftHand);
                Instantiate(equippedPrefab, handTransform);
            }
            if (animatorOverride != null)
            {
                animator.runtimeAnimatorController = animatorOverride;
            }
        }

        private Transform GetTransform(Transform rightHand, Transform leftHand)
        {
            Transform handTransform;
            if (isRightHanded) handTransform = rightHand;
            else handTransform = leftHand;
            return handTransform;
        }

        public bool HasProjectile()
        {
            return projectile != null;
        }

        public void LaunchProjectile(Transform rightHand, Transform leftHand, Health target)
        {
            Projectile projectileInstance = Instantiate(projectile, GetTransform(rightHand, leftHand).position, Quaternion.identity);
            projectileInstance.SetTarget(target, weaponDamage); //weaponDamageを追加
        }

        public float GetDamage()
        {
            return weaponDamage;
        }

        public float GetRange()
        {
            return weaponRange;
        }
    }
}

2, ArrowProjectileにBoxColliderをつけ、サイズ調整、IsTriggerを付ける
A776E65B-C782-428C-AB1E-3D95E48B0941.jpeg

矢にエフェクトを実装

1, ArrowProjecttileにTrailを作成する
B1BB165C-BCC2-4C47-A2E7-B748D0ABB343.jpeg

2, TrailのTime、Widthの数値を決める
BBC38AAC-A383-43C8-83FB-F8EBBEDCB2BD.jpeg

3, ReceiveShadowのチェックを外す
2DA1C22F-AE92-46F0-BE6B-845EA28C8EB6_4_5005_c.jpeg

4, EnemyをPrefabVariantしてArcherを作成する
06D40913-9DB2-4AD4-B7F3-B9E9840554E3.jpeg

5, ArcherにWeaponを設定する
9AA99BE2-16EF-4226-983C-97026FD71BE6_4_5005_c.jpeg

6, ArrowProjectileをPrefabVariantしてRedとBlueを作成
B355D041-3181-47D0-A39A-111B4C7B9790.jpeg
305818F0-4325-4DC9-826A-DF972367E537_4_5005_c.jpeg

7, 各Trailのカラーを設定する
9177D8A3-AD0C-49F2-8C35-C6F7F768BB1D.jpeg

8, Axeを複製し、AxeBlueProjectileとAxeRedProjectileを作成
9B71E5BF-642B-4A23-998B-BB489432AB7A_4_5005_c.jpeg

9, 各AxeRedProjectile、Blueにオブジェクトをアタッチする
DA860277-76C1-4D5C-81B5-EC1F3935AA9B.jpeg

10, Archer,PlayerそれぞれにWeaponを設定する
CABFACE0-1F94-490F-AA10-318DC4E03EE2.jpeg

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1