Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / activity / ActivityInstance.cs
@王晗智 王晗智 on 5 Mar 2024 9 KB 2

using System;
using System.IO;
using Godot;

/// <summary>
/// 用于在预制场景中创建 ActivityObject
/// </summary>
[Tool]
public partial class ActivityInstance : Node2D
{
	private const string GroupName = "Editor";
	
	/// <summary>
	/// 物体Id
	/// </summary>
	[Export]
	public string Id
	{
		get => _id;
		set
		{
			_id = value;
			_dirty = true;
		}
	}

	/// <summary>
	/// 默认所在层级
	/// </summary>
	[Export]
	public RoomLayerEnum DefaultLayer { get; set; } = RoomLayerEnum.NormalLayer;

	/// <summary>
	/// 是否显示阴影
	/// </summary>
	[Export]
	public bool ShowShadow
	{
		get => _showShadow;
		set
		{
			_showShadow = value;
			if (_activityObject != null)
			{
				if (value)
				{
					_activityObject.ShowShadowSprite();
				}
				else
				{
					_activityObject.HideShadowSprite();
				}
			}
		}
	}

	/// <summary>
	/// 阴影偏移
	/// </summary>
	[Export]
	public Vector2 ShowOffset
	{
		get => _showOffset;
		set
		{
			_showOffset = value;
			if (_activityObject != null)
			{
				_activityObject.ShadowOffset = value;
			}
		}
	}

	/// <summary>
	/// 初始海拔高度
	/// </summary>
	[Export]
	public float Altitude
	{
		get => _altitude;
		set
		{
			_altitude = value;
			if (_activityObject != null)
			{
				_activityObject.Altitude = value;
				_activityObject.Collision.Position = _collPos;
				_activityObject.UpdateShadowSprite((float)GetProcessDeltaTime());
				_activityObject.CalcThrowAnimatedPosition();
			}
		}
	}

	/// <summary>
	/// 动画精灵的z轴索引
	/// </summary>
	[Export]
	public int SpriteZIndex
	{
		get => _spriteZIndex;
		set
		{
			_spriteZIndex = value;
			if (_activityObject != null)
			{
				_activityObject.AnimatedSprite.ZIndex = value;
			}
		}
	}
	
	/// <summary>
	/// 阴影z轴索引
	/// </summary>
	[Export]
	public int ShadowZIndex
	{
		get => _shadowZIndex;
		set
		{
			_shadowZIndex = value;
			if (_activityObject != null)
			{
				_activityObject.ShadowSprite.ZIndex = value;
			}
		}
	}
	
	/// <summary>
	/// 是否启用垂直运动模拟
	/// </summary>
	[Export]
	public bool VerticalMotion { get; private set; } = true;

	/// <summary>
	/// 是否启用碰撞器
	/// </summary>
	[Export]
	public bool CollisionEnabled
	{
		get => _collisionEnabled;
		set
		{
			_collisionEnabled = value;
			if (_activityObject != null)
			{
				_activityObject.Collision.Disabled = !value;
			}
		}
	}
	
	/// <summary>
	/// 编辑器属性, 物体子碰撞器在编辑器中是否可见
	/// </summary>
	[Export]
	public bool CollisionVisible
	{
		get => _collisionVisible;
		set
		{
			_collisionVisible = value;
			OnChangeCollisionVisible();
		}
	}

	private bool _dirty = false;
	private bool _collisionVisible = true;
	private string _prevId;
	private string _id;
	private ActivityObject _activityObject;
	private Sprite2D _errorSprite;
	private bool _showShadow = true;
	private Vector2 _showOffset = new Vector2(0, 2);
	private float _altitude;
	private int _spriteZIndex = 0;
	private int _shadowZIndex = -1;
	private bool _collisionEnabled = true;

	private Vector2 _collPos;
	private bool _createFlag = false;
	
	//嵌套Instance相关
	private bool _isNested = false;
	private ActivityObject _activityInstance;
	
	private static string _jsonText;

	/// <summary>
	/// 清空缓存的json
	/// </summary>
	public static void ClearCacheJson()
	{
		_jsonText = null;
	}
	
	public override void _Ready()
	{
#if TOOLS
		if (!Engine.IsEditorHint())
		{
#endif
			var world = World.Current;
			if (world != null && world.YSortLayer != null && world.NormalLayer != null)
			{
				DoCreateObject();
			}
#if TOOLS
		}
#endif
	}

	public override void _Process(double delta)
	{
#if TOOLS
		if (Engine.IsEditorHint())
		{
			if (_dirty || (_activityObject != null && _activityObject.GetParent() != this))
			{
				_dirty = false;
				
				if (_prevId != _id)
				{
					OnChangeActivityId(_id);
				}
				else if (string.IsNullOrEmpty(_id))
				{
					ShowErrorSprite();
				}

				OnChangeCollisionVisible();
			}

			if (_activityObject != null)
			{
				_activityObject.Collision.Position = _collPos;
				_activityObject.UpdateShadowSprite((float)delta);
				_activityObject.CalcThrowAnimatedPosition();
			}
		}
		else
		{
#endif
			var world = World.Current;
			if (world != null && world.YSortLayer != null && world.NormalLayer != null)
			{
				DoCreateObject();
			}
#if TOOLS
		}
#endif
	}

	public override void _EnterTree()
	{
#if TOOLS
		if (Engine.IsEditorHint())
		{
			_dirty = true;
			
			var children = GetChildren();
			foreach (var child in children)
			{
				if (child is ActivityObject)
				{
					child.QueueFree();
				}
			}
			if (_activityObject != null)
			{
				_activityObject.QueueFree();
			}
			_activityObject = null;
			_prevId = null;
		}
#endif
	}

	public override void _ExitTree()
	{
#if TOOLS
		if (Engine.IsEditorHint() && _activityObject != null)
		{
			_activityObject.QueueFree();
			_activityObject = null;
		}
#endif
	}

	private ActivityObject DoCreateObject()
	{
		if (_createFlag)
		{
			return _activityInstance;
		}

		_createFlag = true;
		var activityObject = ActivityObject.Create(Id);
		if (_isNested)
		{
			activityObject.Position = Position - new Vector2(0, 1);
			activityObject.Scale = Scale;
			activityObject.Rotation = Rotation;
		}
		else
		{
			activityObject.Position = GlobalPosition + new Vector2(0, 1);
			activityObject.Scale = GlobalScale;
			activityObject.Rotation = GlobalRotation;
		}
		
		activityObject.Visible = Visible;
		activityObject.ShadowOffset = _showOffset;
		activityObject.Altitude = _altitude;
		activityObject.AnimatedSprite.ZIndex = _spriteZIndex;
		activityObject.ShadowSprite.ZIndex = _shadowZIndex;
		activityObject.EnableVerticalMotion = VerticalMotion;
		activityObject.Collision.Disabled = !_collisionEnabled;
		if (!_isNested)
		{
			activityObject.PutDown(DefaultLayer, _showShadow);
		}
		else
		{
			activityObject.DefaultLayer = DefaultLayer;
			activityObject.ShowShadowSprite();
		}

		var children = GetChildren();
		foreach (var child in children)
		{
			if (!child.IsInGroup(GroupName))
			{
				if (child is ActivityInstance o)
				{
					o._isNested = true;
					var instance = o.DoCreateObject();
					activityObject.AddChild(instance);
					if (instance is IMountItem mountItem)
					{
						activityObject.AddMountObject(mountItem);
					}
					else if (instance is IDestroy destroy)
					{
						activityObject.AddDestroyObject(destroy);
					}
				}
				else
				{
					child.Reparent(activityObject);
					if (child is Node2D node2D && activityObject.GetCurrentTexture().GetHeight() % 2 == 0)
					{
						node2D.Position += new Vector2(0, 1);
					}
				}
			}
		}

		QueueFree();
		_activityInstance = activityObject;
		return  activityObject;
	}

	private void OnChangeActivityId(string id)
	{
		_prevId = id;

		if (_activityObject != null)
		{
			_activityObject.QueueFree();
			_activityObject = null;
		}

		if (string.IsNullOrEmpty(id))
		{
			ShowErrorSprite();
			return;
		}

		if (_jsonText == null)
		{
			_jsonText = File.ReadAllText("resource/config/ActivityBase.json");
		}
		var str = $"\"Id\": \"{id}\",";
		var index = _jsonText.IndexOf(str, StringComparison.Ordinal);
		if (index > -1)
		{
			const string s = "\"Prefab\": \"";
			var startIndex = _jsonText.IndexOf(s, index, StringComparison.Ordinal);
			if (startIndex > -1)
			{
				var endIndex = _jsonText.IndexOf('"', startIndex + s.Length + 1);
				if (endIndex > -1)
				{
					var prefab = _jsonText.Substring(startIndex + s.Length, endIndex - (startIndex + s.Length));
					var instance = ResourceManager.LoadAndInstantiate<ActivityObject>(prefab);
					_activityObject = instance;
					_collPos = instance.Collision.Position - instance.AnimatedSprite.Position - instance.AnimatedSprite.Offset;
					instance.IsCustomShadowSprite = instance.ShadowSprite.Texture != null;
					instance.Altitude = _altitude;
					instance.ShadowOffset = _showOffset;
					_activityObject.Position = _activityObject.AnimatedSprite.Position;
					if (_showShadow)
					{
						instance.ShowShadowSprite();
						var shadowSpriteMaterial = instance.ShadowSprite.Material as ShaderMaterial;
						if (shadowSpriteMaterial != null)
						{
							shadowSpriteMaterial.SetShaderParameter(
								ShaderParamNames.ShowOutline,
								((ShaderMaterial)instance.AnimatedSprite.Material).GetShaderParameter(ShaderParamNames
									.ShowOutline)
							);
						}
					}
					AddChild(instance);
					MoveChild(instance, 0);
					HideErrorSprite();
					return;
				}
			}
		}
		GD.PrintErr($"未找到Id为'{id}'的物体!");
		ShowErrorSprite();
	}

	private void OnChangeCollisionVisible()
	{
		if (_activityObject != null)
		{
			Utils.EachNode(_activityObject, node =>
			{
				if (node is CollisionShape2D collisionShape2D)
				{
					collisionShape2D.Visible = _collisionVisible;
				}
				else if (node is CollisionPolygon2D collisionPolygon2D)
				{
					collisionPolygon2D.Visible = _collisionVisible;
				}
			});
		}
	}

	private void ShowErrorSprite()
	{
		if (_errorSprite == null)
		{
			_errorSprite = new Sprite2D();
			_errorSprite.AddToGroup(GroupName);
			_errorSprite.Texture = ResourceManager.LoadTexture2D(ResourcePath.resource_sprite_ui_commonIcon_Error_mini_png);
			AddChild(_errorSprite);
			MoveChild(_errorSprite, GetChildCount() - 1);
		}
	}

	private void HideErrorSprite()
	{
		if (_errorSprite != null)
		{
			_errorSprite.QueueFree();
			_errorSprite = null;
		}
	}
}