先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
339 lines
13 KiB
C#
339 lines
13 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.AddressableAssets;
|
|
using cfg;
|
|
using asap.core;
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
public class BlockManager : MonoBehaviour
|
|
{
|
|
private Dictionary<string, GameObject> _blockPrefabs = new Dictionary<string, GameObject>();
|
|
//[SerializeField] private BoxCollider2D _deathWall;
|
|
[SerializeField] List<Block> _blocks = new List<Block>();
|
|
[SerializeField] private Transform _focus;
|
|
public Block CurrentBlock;
|
|
private TbConstructionBasicUnits _blockTypesTable;
|
|
private TbConstructionUnits _blocksTable;
|
|
private TbConstructionHeightEvent _heightEventTable;
|
|
private InfiniteBuildingConf _cfg;
|
|
private InfiniteBuildingData _data;
|
|
private EventAggregator _eventAggregator;
|
|
private List<AsyncOperationHandle> _prefabOps;
|
|
public List<Block> Blocks => _blocks;
|
|
private void Start()
|
|
{
|
|
_blockTypesTable = GContext.container.Resolve<Tables>().TbConstructionBasicUnits;
|
|
_blocksTable = GContext.container.Resolve<Tables>().TbConstructionUnits;
|
|
_prefabOps = new List<AsyncOperationHandle>();
|
|
}
|
|
private void OnDestroy()
|
|
{
|
|
foreach (var op in _prefabOps)
|
|
Addressables.Release(op);
|
|
}
|
|
public void Init(InfiniteBuildingConf cfg, InfiniteBuildingData data, EventAggregator ea)
|
|
{
|
|
_cfg = cfg;
|
|
_data = data;
|
|
_eventAggregator = ea;
|
|
_heightEventTable = GContext.container.Resolve<Tables>().TbConstructionHeightEvent;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Get prefab according to PrefabID, will be used in init and block generation
|
|
/// </summary>
|
|
/// <param name="id">name of the prefab in addressables</param>
|
|
/// <returns>the prefab GameObject</returns>
|
|
private async System.Threading.Tasks.Task<GameObject> GetBlockPrefab(string id)
|
|
{
|
|
if (_blockPrefabs.TryGetValue(id, out var prefab))
|
|
return prefab;
|
|
_prefabOps.Add(Addressables.LoadAssetAsync<object>(id));
|
|
var go = (GameObject) await _prefabOps[^1].Task;
|
|
_blockPrefabs.Add(id, go);
|
|
return go;
|
|
}
|
|
private int GetRandomIDFromWeightTable(Dictionary<int, float> weightTable)
|
|
{
|
|
Dictionary<int, float> weightSum = new Dictionary<int, float>();
|
|
float sum = 0;
|
|
foreach (var e in weightTable)
|
|
{
|
|
sum += e.Value;
|
|
weightSum.Add(e.Key, sum);
|
|
}
|
|
float randomNum = UnityEngine.Random.Range(0, sum);
|
|
foreach (var e in weightSum)
|
|
{
|
|
if (randomNum <= e.Value)
|
|
{
|
|
return e.Key;
|
|
}
|
|
}
|
|
Debug.LogError($"Random ID not available. WeightTable entries count: {weightTable.Count}.");
|
|
return -1;
|
|
}
|
|
public async System.Threading.Tasks.Task<Block> GenerateBlock(Vector3 spawnPos, bool isFirst = false)
|
|
{
|
|
Block generatedBlock;
|
|
Dictionary<int, float> weightTable = new Dictionary<int, float>();
|
|
//_crane.ResetPosition();
|
|
//await Awaiters.NextFrame;
|
|
int type, block;
|
|
foreach (var bt in _blockTypesTable.DataList)
|
|
{
|
|
weightTable.Add(bt.ID, bt.Weight);
|
|
}
|
|
type = GetRandomIDFromWeightTable(weightTable);
|
|
weightTable.Clear();
|
|
foreach (int blockID in _blockTypesTable[type].UnitList)
|
|
{
|
|
if (_data.Lvl < _blocksTable[blockID].UnlockedHeight)
|
|
continue;
|
|
weightTable.Add(blockID, _blocksTable[blockID].Weight);
|
|
}
|
|
block = GetRandomIDFromWeightTable(weightTable);
|
|
if (isFirst && _data.LastHangingBlock != -1)
|
|
{
|
|
block = _data.LastHangingBlock;
|
|
_data.LastHangingBlock = -1;
|
|
}
|
|
TbConstructionInfiniteInit initTable = GContext.container.Resolve<Tables>().TbConstructionInfiniteInit;
|
|
if (_data.Lvl < initTable.DataList.Count)
|
|
block = GContext.container.Resolve<Tables>().TbConstructionInfiniteInit.DataList[_data.Lvl].UnitID;
|
|
#if UNITY_EDITOR
|
|
if (_cfg.NextBlockToSpawn != -1 && _blocksTable.DataMap.ContainsKey(_cfg.NextBlockToSpawn))
|
|
block = _cfg.NextBlockToSpawn;
|
|
#endif
|
|
string id = _blocksTable[block].Prefab;
|
|
GameObject prefab = await GetBlockPrefab(id);
|
|
//GameObject prefab = await GetBlockPrefab("block_000");//Get test prefab
|
|
//Debug.Log($"<color=red>{_crane.transform.position}</color>");
|
|
//Debug.Log($"<color=red>{_cfg.blockSpawnOffset}</color>");
|
|
generatedBlock = Instantiate(prefab, spawnPos + _blocksTable[block].Height * Vector3.down,
|
|
prefab.transform.rotation, transform).GetComponent<Block>();
|
|
generatedBlock.InitHangingBlock(BlockState.Hanging, block, _eventAggregator, _cfg);
|
|
generatedBlock.Rb.angularDrag = GetDataByIBLvl(_data.Lvl, _heightEventTable.DataList).AngularDrag;
|
|
generatedBlock.Rb.drag = GetDataByIBLvl(_data.Lvl, _heightEventTable.DataList).LinearDrag;
|
|
//_currentBlock.SetParams(_cfg, _deathWall.name, block, _focus);
|
|
//generatedBlock.OnRest += async d => await OnBlockRest(d);
|
|
//generatedBlock.OnDestroy += async () => await OnBlockDestroy();
|
|
//_currentBlock.AttachJoints(_crane);
|
|
//_crane.AttachJoints(generatedBlock);
|
|
//await _crane.Descend();
|
|
//if (doSwitchState)
|
|
// _stateMachine.SetState(EInfiniteBuildingState.PlayerCtrl);
|
|
return generatedBlock;
|
|
}
|
|
private async System.Threading.Tasks.Task<GameObject> GenerateBlock(InfiniteBuildingData.BlockRecord record, IEventAggregator ea)
|
|
{
|
|
GameObject b = Instantiate(await GetBlockPrefab(record.PrefabID),
|
|
new Vector3(record.Pos.x, record.Pos.y, _cfg.blockDepth), Quaternion.identity, transform);
|
|
b.GetComponent<Rigidbody2D>().isKinematic = true;
|
|
b.GetComponent<Rigidbody2D>().velocity = Vector2.zero;
|
|
b.GetComponent<Block>().Idx = record.ID;
|
|
b.GetComponent<Block>().EventAggregator = ea;
|
|
//_blocks[^1].SetParams(_cfg, _deathWall.name, record.id, _focus);
|
|
return b;
|
|
}
|
|
|
|
public int GetExcellentScore()
|
|
{
|
|
int fallingBlockScore = _blocksTable[CurrentBlock.Idx].FallPoint;
|
|
int stackedBlockScore = _blocks is { Count: > 0 } ? _blocksTable[_blocks[^1].Idx].StackPoint : 0;
|
|
// Debug.Log($"<color=red>Incoming: {fallingBlockScore}, stacked: {stackedBlockScore}</color>");
|
|
return fallingBlockScore + stackedBlockScore;
|
|
}
|
|
public bool TrySnap()
|
|
{
|
|
if (_blocks is not { Count: > 0 })
|
|
{
|
|
if (Mathf.Abs(CurrentBlock.Rb.position.x - _focus.position.x) > _cfg.snapThresh)
|
|
return false;
|
|
CurrentBlock.Rb.MovePosition(new Vector2(_focus.position.x, CurrentBlock.Rb.position.y));
|
|
return true;
|
|
}
|
|
if (Mathf.Abs(CurrentBlock.Rb.position.x - _blocks[^1].Rb.position.x) > _cfg.snapThresh)
|
|
return false;
|
|
CurrentBlock.Rb.MovePosition(new Vector2(_blocks[^1].Rb.position.x, CurrentBlock.Rb.position.y));
|
|
return true;
|
|
}
|
|
public async void AlignBlocks(IEventAggregator ea)
|
|
{
|
|
Block b;
|
|
for (int j = _blocks.Count - 1; j >= 0; j--)
|
|
{
|
|
b = _blocks[j];
|
|
//_blocks.RemoveAt(j);
|
|
if (b != null)
|
|
Destroy(b.gameObject);
|
|
}
|
|
Vector3 pivot = _focus.position;
|
|
for (int i = _data.Record.Count - 1; i >= 0; i--)
|
|
{
|
|
InfiniteBuildingData.BlockRecord r = _data.Record[i];
|
|
b = Instantiate(await GetBlockPrefab(r.PrefabID),
|
|
pivot + r.Height * Vector3.down,
|
|
Quaternion.identity, transform).GetComponent<Block>();
|
|
pivot += r.Height * Vector3.down;
|
|
//_blocks[_data.Record.Count - 1 - i] = b;
|
|
_blocks[i] = b;
|
|
b.Rb.isKinematic = true;
|
|
b.Rb.velocity = Vector2.zero;
|
|
b.Idx = r.ID;
|
|
b.EventAggregator = ea;
|
|
}
|
|
for (int i = _blocks.Count - 1; i >= 0; i--)
|
|
{
|
|
b = _blocks[i];
|
|
if (b.WorldHeight < _focus.position.y + _cfg.blockDestroyOffset)
|
|
{
|
|
_blocks.RemoveAt(i);
|
|
Destroy(b.gameObject);
|
|
}
|
|
else if (b.WorldHeight < _focus.position.y + _cfg.blockRestingOffset)
|
|
{
|
|
b.Rb.isKinematic = true;
|
|
b.State = BlockState.Locked;
|
|
}
|
|
else
|
|
{
|
|
b.Rb.isKinematic = false;
|
|
b.State = BlockState.Resting;
|
|
}
|
|
}
|
|
//for (int i = 0; i < _blocks.Count; i++)
|
|
//{
|
|
// if (_blocks[i] == null)
|
|
// continue;
|
|
// _blocks[i].Rb.MovePosition(new Vector2(0, _data.Record[i].pos.y));
|
|
//}
|
|
}
|
|
private float _blockDestructionTimer = 1000f;
|
|
public void BlockFallFixedUpdate()
|
|
{
|
|
float rot = CurrentBlock.Rb.rotation;
|
|
_blockDestructionTimer -= Time.fixedDeltaTime;
|
|
if (_blockDestructionTimer < 0)
|
|
{
|
|
_eventAggregator.Publish(new EventInfiniteBuildingFail());
|
|
return;
|
|
}
|
|
if (!CurrentBlock.Freezable || !CurrentBlock.IsCurrentBlockStill)
|
|
return;
|
|
|
|
if ((rot <= _cfg.blockDestroyThreshAngle && rot > -_cfg.blockDestroyThreshAngle)
|
|
|| (rot >= 180 - _cfg.blockDestroyThreshAngle && rot <= 180)
|
|
|| (rot >= -180 && rot <= -180 + _cfg.blockDestroyThreshAngle))
|
|
_eventAggregator.Publish(new EventInfiniteBuildingSucceed());
|
|
else
|
|
_eventAggregator.Publish(new EventInfiniteBuildingFail());
|
|
}
|
|
public void StartDestroyCountDown()
|
|
{
|
|
//Debug.Log("111111111111111");
|
|
_blockDestructionTimer = _cfg.blockDestroyDelay;
|
|
}
|
|
public void ResetDestroyCountDown()
|
|
{
|
|
_blockDestructionTimer = float.PositiveInfinity;
|
|
}
|
|
public void AddCurrentBlock()
|
|
{
|
|
_blocks.Add(CurrentBlock);
|
|
}
|
|
private void CurrentBlockFail()
|
|
{
|
|
Debug.Log("Build failure.");
|
|
}
|
|
/// <summary>
|
|
/// Lock or clear blocks according to their height, need focus updated first
|
|
/// </summary>
|
|
public void LockOrClearBlocks(bool isFilterOnly = false)
|
|
{
|
|
for (int i = _blocks.Count - 1; i >= 0; i--)
|
|
{
|
|
Block b = _blocks[i];
|
|
if (b.WorldHeight < _cfg.blockDestroyOffset + _focus.position.y)
|
|
{
|
|
_blocks.RemoveAt(i);
|
|
Destroy(b.gameObject);
|
|
}
|
|
else if (b.WorldHeight < _cfg.blockRestingOffset + _focus.position.y )
|
|
{
|
|
b.Rb.velocity = Vector2.zero;
|
|
b.Rb.rotation = 0;
|
|
b.Rb.angularVelocity = 0;
|
|
b.Rb.isKinematic = true;
|
|
}
|
|
else if (!isFilterOnly)
|
|
b.Rb.isKinematic = false;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Clear stage, generate blocks according to data, and reset focus position
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
public async System.Threading.Tasks.Task RebuildAllBlocks()
|
|
{
|
|
//clear the act
|
|
if (CurrentBlock != null)
|
|
Destroy(CurrentBlock.gameObject);
|
|
await RebuildBlocks();
|
|
}
|
|
/// <summary>
|
|
/// reset stage, bug leave the current block alone
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public async System.Threading.Tasks.Task RebuildBlocks(bool doesLockAll = false)
|
|
{
|
|
Block b;
|
|
for (int j = _blocks.Count - 1; j >= 0; j--)
|
|
{
|
|
b = _blocks[j];
|
|
_blocks.RemoveAt(j);
|
|
if (b != null)
|
|
Destroy(b.gameObject);
|
|
}
|
|
//regenerate
|
|
_focus.position = new Vector3(_focus.position.x, 0, _focus.position.z);
|
|
foreach (var record in _data.Record)
|
|
{
|
|
b = (await GenerateBlock(record, _eventAggregator)).GetComponent<Block>();
|
|
b.InitRecordBlock(_cfg);
|
|
b.LitBlock();
|
|
_blocks.Add(b);
|
|
if (_focus.position.y < b.WorldHeight)
|
|
_focus.position = new Vector3(_focus.position.x, b.WorldHeight, _focus.position.z);
|
|
}
|
|
for (int i = _blocks.Count - 1; i >= 0; i--)
|
|
{
|
|
b = _blocks[i];
|
|
if (b.WorldHeight < _focus.position.y + _cfg.blockDestroyOffset)
|
|
{
|
|
_blocks.RemoveAt(i);
|
|
Destroy(b.gameObject);
|
|
}
|
|
else if (b.WorldHeight < _focus.position.y + _cfg.blockRestingOffset || doesLockAll)
|
|
{
|
|
b.Rb.isKinematic = true;
|
|
b.State = BlockState.Locked;
|
|
}
|
|
else
|
|
{
|
|
b.Rb.isKinematic = false;
|
|
b.State = BlockState.Resting;
|
|
}
|
|
}
|
|
}
|
|
private ConstructionHeightEvent GetDataByIBLvl(int lvl, List<ConstructionHeightEvent> dataList)
|
|
{
|
|
for (int i = 0; i < dataList.Count - 1; i++)
|
|
{
|
|
if (lvl < dataList[i + 1].Height)
|
|
return dataList[i];
|
|
}
|
|
return dataList[^1];
|
|
}
|
|
}
|