问题描述
我想创建一个带有下拉菜单的自定义检查器。我想这样做是为了在从基接口继承的多个类之间进行选择。
因此,我创建了一个脚本,该脚本覆盖了扩展 ScriptableObject 的 CreatureSO 类的默认编辑器。我知道有两种方法可以使用目标变量和序列化对象访问类 Creature 的属性。我想使用 serializedobject 方法,因为 它包含的功能。
我当前的代码:
CreatureSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Creature",menuName = "Custom/Creature/Instance")]
public class CreatureSO : ScriptableObject
{
public Sprite sprite;
public Vector3 position;
public Vector2 size;
public CreatureStats stats;
//[HideInInspector]
[SerializeReference] public IEngine engine; //this is the property
}
IEngine.cs
using System;
using UnityEngine;
using UnityEngine.InputSystem;
public interface IEngine
{
}
PlayerEngine.cs
using System;
using UnityEngine;
using UnityEngine.InputSystem;
[System.Serializable]
public class PlayerEngine: IEngine
{
private Creature _creature;
private Keyboard keyboard;
private bool inputCtrl;
public void Start(Creature creature)
{
keyboard = Keyboard.current;
_creature = creature;
}
private void FixedUpdate()
{
//Movement
_creature.isMovingX = keyboard.aKey.ispressed ^ keyboard.dKey.ispressed;
_creature.inputX = !_creature.isMovingX ? 0 : keyboard.aKey.ispressed ? -1 : 1;
_creature.isWalking = _creature.isMovingX && _creature.isGrounded && !inputCtrl;
_creature.isRunning = _creature.isMovingX && _creature.isGrounded && inputCtrl;
_creature.isDashing = keyboard.sKey.ispressed;
_creature.direction = _creature.inputX != 0 ? (int)_creature.inputX : _creature.direction;
//wall;
_creature.isWalled = _creature.controller.collisionInfo.left || _creature.controller.collisionInfo.right;
_creature.isFullWalled = _creature.controller.collisionInfo.fullLeft || _creature.controller.collisionInfo.fullRight;
//Jump
_creature.isJumping = keyboard.spaceKey.ispressed;
//attack
_creature.isAttacking = false;
//if (_creature.stats.canAttack)
{
_creature.isAttacking = keyboard.wKey.ispressed;
// _creature.stats.canAttack = false;
}
if (!keyboard.wKey.ispressed)
//_creature.stats.canAttack = true;
//running
inputCtrl = keyboard.leftCtrlKey.ispressed;
//vapor
//_creature.stats.hasVapor = _creature.stats.vaporCount > 0;
//Habilities
//dashHab.FixedUpdate(this);
}
}
EnemyEngine
public class EnemyEngine: IEngine
{
private Creature _creature;
public void Init(Creature creature)
{
_creature = creature;
}
public void Update()
{
//Currently this engine does nothing
//In the future it will detect his surroundings and command some actions to the Creature.
//for example: (pseudocode)
// if( ! Raycast( front ) ) {
//
// Creature.move.forward();
//}
}
}
CreatureSO.cs
using UnityEngine;
using UnityEditor;
enum EngineType { Player,Enemy };
[CustomEditor(typeof(CreatureSO))]
public class CreatureSOEditor : Editor
{
EngineType engineType;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedobject.Update();
SerializedProperty engine = serializedobject.FindProperty("engine");
engineType = (EngineType)EditorGUILayout.EnumPopup("Engine",engineType);
engine.objectReferenceValue = SelectEngine();
serializedobject.ApplyModifiedProperties();
}
public IEngine SelectEngine()
{
switch(engineType)
{
case EngineType.Player:
PlayerEngine playerEngine = new PlayerEngine();
return playerEngine;
default:
return null;
}
}
}
Creature.cs
using System;
using System.Collections.Generic;
using UnityEngine;
public class Creature: MonoBehavIoUr
{
public CreatureSO instance;
public IHability[] _actions;
public IEngine engine;
//public ItemPickup itemToPick;
//public GameState gameState;
//public Inventory inventory;
[HideInInspector] private GameObject _actionsGO;
[HideInInspector] private CreatureStats stats;
[HideInInspector] public BoxCollider2D BoxCollider;
[HideInInspector] public Controller2D controller;
[HideInInspector] public Animator animator;
[HideInInspector] public GameObject spriteObject;
[HideInInspector] public SpriteRenderer spriteRenderer;
public Collider2D attackCollider;
public Collider2D bodyCollider;
public Vector2 veLocity;
public int direction;
public float inputX;
public bool isMovingX;
public bool isWalking;
public bool isRunning;
public bool isJumping;
public bool isAttacking;
public bool isGrounded;
public bool isWalled;
public bool isFullWalled;
public bool isDashing;
public bool canPickup;
private ContactFilter2D contactFilter;
public void Start()
{
BoxCollider = gameObject.AddComponent<BoxCollider2D>();
BoxCollider.size = instance.size;
controller = Controller2D.Attach(gameObject,BoxCollider);
spriteObject = new GameObject();
spriteObject.transform.parent = gameObject.transform;
spriteObject.transform.localPosition = new Vector3();
spriteObject.transform.localScale = new Vector3(instance.size.x/10,instance.size.y/10);
spriteRenderer = spriteObject.AddComponent(typeof(SpriteRenderer)) as SpriteRenderer;
spriteRenderer.sprite = instance.sprite;
spriteRenderer.size = instance.size;
animator = gameObject.AddComponent(typeof(Animator)) as Animator;
stats = instance.stats;
_actionsGO = new GameObject("Actions");
_actionsGO.transform.SetParent(this.transform);
ScriptableObject[] actionStats = stats.actionStats;
_actions = new IHability[actionStats.Length];
IHability currentAction;
for (int i =0; i < actionStats.Length; i++)
{
switch (actionStats[i])
{
case WalkingStats stats:
currentAction = _actionsGO.AddComponent<WalkingHab>();
((WalkingHab)currentAction).Init(this,(WalkingStats)actionStats[i]);
break;
case FallingStats stats:
currentAction = _actionsGO.AddComponent<FallingHab>();
((FallingHab)currentAction).Init(this,(FallingStats)actionStats[i]);
break;
case JumpingStats stats:
currentAction = _actionsGO.AddComponent<JumpHab>();
((JumpHab)currentAction).Init(this,(JumpingStats)actionStats[i]);
break;
case AirMovingStats stats:
currentAction = _actionsGO.AddComponent<AirMovingHab>();
((AirMovingHab)currentAction).Init(this,(AirMovingStats)actionStats[i]);
break;
case DashingStats stats:
currentAction = _actionsGO.AddComponent<DashingHab>();
((DashingHab)currentAction).Init(this,(DashingStats)actionStats[i]);
break;
case GrabbingStats stats:
currentAction = _actionsGO.AddComponent<GrabbingHab>();
((GrabbingHab)currentAction).Init(this,(GrabbingStats)actionStats[i]);
break;
case JumpDiagonalStats stats:
currentAction = _actionsGO.AddComponent<JumpDiagonalHab>();
((JumpDiagonalHab)currentAction).Init(this,(JumpDiagonalStats)actionStats[i]);
break;
case AttackStats stats:
currentAction = _actionsGO.AddComponent<AttackHab>();
((AttackHab)currentAction).Init(this,(AttackStats)actionStats[i]);
break;
default:
return;
}
_actions[i] = currentAction;
}
direction = 1;
//this.health = stats.maxHealth;
contactFilter = new ContactFilter2D();
contactFilter.useLayerMask = true;
//contactFilter.layerMask = stats.attackLayerMask;
}
public void Update()
{
}
public void FixedUpdate()
{
isGrounded = controller.collisionInfo.below;
if (isGrounded || controller.collisionInfo.above) veLocity.y = Mathf.Sign(veLocity.y) * 0.1f;
for (int i = 0; i < _actions.Length; i++)
{
if (!_actions[i].CanExecute()) continue;
_actions[i].Execute();
}
if (this.direction != Mathf.Sign(this.transform.localScale.x))
{
this.transform.localScale = Vector3.Scale(this.transform.localScale,new Vector3(-1,1,1));
}
this.transform.Translate(this.controller.Move(veLocity * Time.deltaTime));
veLocity.x = 0;
}
//public abstract void pickupAction(ItemPickup item);
public void setVeLocityX(float veLocityX)
{
this.veLocity.x = veLocityX;
}
public void setVeLocityY(float veLocityY)
{
this.veLocity.y = veLocityY;
}
public void addVeLocityY(float veLocityY)
{
this.veLocity.y += veLocityY;
}
public void addVeLocityX(float veLocityX)
{
//if (veLocity.x < stats.speedXMax)
this.veLocity.x += veLocityX;
}
public void attack()
{
List<Collider2D> enemies = new List<Collider2D>();
Physics2D.OverlapCollider(this.attackCollider,contactFilter,enemies);
foreach (Collider2D enemy in enemies)
{
//Enemy a = enemy.gameObject.GetComponent<Enemy>();
//a.damage(10);
}
}
}
我试图用这些类实现的是创建一个通用对象 Creature,我可以用一个 scriptableObject 轻松定义它。因此,当我按下播放按钮时,所有带有 Creature 组件的 GameObjects 都会使用列表中的一些 CreatureSO。引擎是生物的大脑,它定义了生物会做什么。我希望生物有不同的行为,这就是为什么我需要不同的引擎。
这是我目前的代码。我的问题是我无法正确地将任何 IEngine 对象转换为 serializedProperty。
Assets\3_Game\Core\ScriptableObjects\Editor\CreatureSOEditor.cs(18,39): error CS0266:
Cannot implicitly convert type 'IEngine' to 'UnityEngine.Object'.
An explicit conversion exists (are you missing a cast?)
using (object) cast 也会出错。有什么建议吗??
另外,是否有任何学习编辑器脚本的资源?不是统一学习平台。
谢谢。
解决方法
这(还)不是一个完整的答案,因为我们需要知道 PlayerEngine
和 EnemyEngine
究竟是什么样子。
根据目前的信息,在
engine.objectReferenceValue = SelectEngine();
SerializedProperty.objectReferenceValue
顾名思义需要一个 UnityEngine.Object
类型的引用,例如GameObject
、Component
、ScriptableObject
以及基本上所有内置资产类型。
不要与 System.Object
混淆,别名 object
在 c#
中代表!
您实现 IEngine
的类都没有继承自 UnityEngine.Object
。
而且总的来说,接口不会从任何一个继承。
即使可以,您很可能也不希望每次完成 Inspector 序列化时都创建一个新实例。
为了提供此问题的可能解决方案,我们需要详细了解您的 IEngine
实现之间的确切区别。