[統合]積和ポインタ、マウスカーソルの作成


リソースの読み込み


https://assetstore.unity.com/packages/3d/characters/animals/dog-knight-pbr-polyart-135227
上のリソースを敵として使用しました.
https://assetstore.unity.com/packages/2d/textures-materials/basic-rpg-cursors-139404
上のリソースをカーソルとして使用します.

臍帯の作成


敵とプレイヤーの戦いのために、臍帯を管理するシナリオを生成する必要がある.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Stat : MonoBehaviour
{
    [SerializeField]
    protected int _level;
    [SerializeField]
    protected int _hp;
    [SerializeField]
    protected int _maxHp;
    [SerializeField]
    protected int _attack;
    [SerializeField]
    protected int _defense;
    [SerializeField]
    protected float _moveSpeed;

    public int Level { get { return _level; } set { _level = value; } }
    public int Hp { get { return _hp; } set { _hp = value; } }
    public int MaxHp { get { return _maxHp; } set { _maxHp = value; } }
    public int Attack { get { return _attack; } set { _attack = value; } }
    public int Defense { get { return _defense; } set { _defense = value; } }
    public float MoveSpeed { get { return _moveSpeed; } set { _moveSpeed = value; } }

    private void Start()
    {
        _level = 1;
        _hp = 100;
        _maxHp = 100;
        _attack = 10;
        _defense = 5;
        _moveSpeed = 5.0f;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerStat : Stat
{
    [SerializeField]
    protected int _exp; 
    [SerializeField]
    protected int _gold;

    public int Exp { get { return _exp; } set { _exp = value; } }
    public int Gold { get { return _gold; } set { _gold = value; } }
    private void Start()
    {
        _level = 1;
        _hp = 100;
        _maxHp = 100;
        _attack = 10;
        _defense = 5;
        _moveSpeed = 5.0f;
        _exp = 0;
        _gold = 0;
    }
}
プレイヤーのみが別のロケータを必要とするため、PlayerStatというスクリプトを生成してプレイヤーのロケータを管理する.
以降のプレイヤーのPrefabにPlayer Statスクリプトを追加し、追加したばかりの敵のPrefapに通常のStatスクリプトを追加します.

マウスカーソル


ゲーム中に敵をクリックしたり、マウスを押して敵の上にぶら下がったりする場合を単独で処理したい.そのため、敵軍のレイヤーをモンスターに設定する.また、便宜上、既存のWall層をGroundに変更する.
コードを変更する前に、アセットストアにロードされたカーソルアセットのテクスチャタイプをCursorに変更します.バージョンの設定では、デフォルトのカーソルを変更できますが、状況に応じてカーソルの形状を変更し続けたいため、カーソルを変更するためにコードを別途作成する必要があります.
Player Controlのコードを変更する必要があります.その前に、Layerの管理を容易にするために、まずDefineクラスにenumを追加します.
public enum Layer
{
    Monster = 8,
    Ground = 9,
    Block = 10,
}
Player Controlを変更します.
/*
[SerializeField]
float _speed = 10.0f;
위 부분을 아래 코드로 대체한다.
*/
PlayerStat _stat;

Texture2D _attackIcon;
Texture2D _handIcon;
// 커서의 모양을 바꾸기 위해 커서의 텍스쳐를 저장하는 변수를 선언한다.

Vector3 _destPos;


void Start()
{
    _stat = gameObject.GetComponent<PlayerStat>();
    // 스탯 정보를 가져오는 코드를 추가한다.
    _attackIcon = Managers.Resource.Load<Texture2D>("Textures/Cursor/Attack");
    _handIcon = Managers.Resource.Load<Texture2D>("Textures/Cursor/Hand");
    // 위에 선언한 변수에 Resource Manager를 이용해 텍스쳐들을 저장한다.
    Managers.Input.MouseAction -= OnMouseClicked;
    Managers.Input.MouseAction += OnMouseClicked;
    // Action을 두번 구독하는 경우가 생기지 않도록 먼저 구독을 끊고 다시 구독을 한다.
}
まず、Statクラスでプレーヤーの速度を決定することにしたので、以前に使用した変数を削除します.
次に、テクスチャを保存する変数を宣言してカーソルの形状を設定し、Start()関数でテクスチャを変数に保存します.
void Update()
{
    UpadateMouseCursor();
    switch (_state)
    {
        case PlayerState.Idle:
            UpdateIdle();
            break;
        case PlayerState.Moving:
            UpdateMoving();
            break;
        case PlayerState.Die:
            UpdateDie();
            break;
    }
}

void UpadateMouseCursor()
{
    if (_state == PlayerState.Die)
        return;

    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    RaycastHit hit;
    if (Physics.Raycast(ray, out hit, 100.0f, _mask))
    {
        if (hit.collider.gameObject.layer == (int)Define.Layer.Monster)
        {
            Cursor.SetCursor(_attackIcon, new Vector2(_attackIcon.width / 5, 0), CursorMode.Auto);
        }
        else
        {
            Cursor.SetCursor(_handIcon, new Vector2(_handIcon.width / 3, 0), CursorMode.Auto);
        }
    }
}
マウスが敵の上にぶら下がっていても、マウスカーソルが変化することが望ましいので、従来のようにイベントを使って処理するのは限界があります.そのため、Update()関数内でLayecastingを行い、マウスカーソルがモンスターの上にあるかどうかを判断する必要があります.
SetCursor関数の2番目のパラメータは、カーソルアイコンのどの部分がマウスの位置であるかを指定する値です.
int _mask = (1 << (int)Define.Layer.Ground) | (1 << (int)Define.Layer.Monster);

void OnMouseClicked(Define.MouseEvent evt)
{
    if (_state == PlayerState.Die)
        return;

    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    // Debug.DrawRay(Camera.main.transform.position, ray.direction * 100.0f, Color.red, 1.0f);

    RaycastHit hit;
    if (Physics.Raycast(ray, out hit, 100.0f, _mask))
    {
        _destPos = hit.point;
        _state = PlayerState.Moving;

        if (hit.collider.gameObject.layer == (int)Define.Layer.Monster)
        {
            Debug.Log("Monster");
        }
        else
        {
            Debug.Log("Ground");
        }
    }
}
Defineクラスで宣言されたenumを使用してmaskという変数を宣言します.
従来のように、光線投射時に、レイヤの情報をString値でロードするのではなくGetList()関数によりmask変数をパラメータに変換する.
コードを修正し、テストし、予想通りに動作していることを確認します.しかし、カーソルが点滅し続けるエラーは、更新()関数でフレーム単位でカーソルを変更し続けることによるエラーです.
したがって、カーソルがシェイプを変更する必要がない場合は、カーソルを個別に変更しないようにカーソルを変更する必要があります.
enum CursorType
{
    None, 
    Attack,
    Hand,
}

void UpadateMouseCursor()
{
    if (_state == PlayerState.Die)
        return;

    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    RaycastHit hit;
    if (Physics.Raycast(ray, out hit, 100.0f, _mask))
    {
        if (hit.collider.gameObject.layer == (int)Define.Layer.Monster)
        {
            if (_cursorType != CursorType.Attack)
            {
                Cursor.SetCursor(_attackIcon, new Vector2(_attackIcon.width / 5, 0), CursorMode.Auto);
                _cursorType = CursorType.Attack;
            } // 마우스가 적에 위치할 때 커서의 모양이 공격이 아니라면 커서의 모양을 변화한다.
        }
        else
        {
            if (_cursorType != CursorType.Hand)
            {
                Cursor.SetCursor(_handIcon, new Vector2(_handIcon.width / 3, 0), CursorMode.Auto);
                _cursorType = CursorType.Hand;
            } // 마우스가 바닥에 위치할 때 커서의 모양이 일반이 아니라면 커서의 모양을 변화한다.

        }
    }
}
修正後確認エラーが解決しました.