2D横板跳跃游戏笔记(查漏补缺ing...)
1.Compression(压缩质量):可以改为None,不压缩的效果最好,但占用内存
2.Filter Mode(过滤模式):可以选择Point(no filter)
3.Pixels Per Unit:是每个方格所容纳的像素个数
4.切割图集要选择Sprite Mode 为 Multiple
注:Pivot(锚点)也就是图片中心轴
5.在Preferences中的Tile Palette中可以添加更多快捷功能。
6.在animator中的每个animation,可以点击Add Behaviour可以创建该类脚本,进行代码控制。
7.动画脚本写设置动画的函数,然后用其他脚本中调用,Trigger单独调用,Int/float/bool在动画脚本的update中更新。
8.Layer Override Priority是代表碰撞优先级,越高越会优先碰撞。
9.关于数值一般需要两个:maxValue与currentValue。
10.继承关系的父类可以写成虚函数,通过子类进行覆写。
public virtual void Move()
{//父类方法
}
override public void Move()
{base.Move();//依然使用父类中的方法,只是增加新的//子类覆写方法
}
11.如果只是在子父之间使用的变量可以使用protected修饰符 。
12.如果合并的碰撞体无法检测,可以调整Composite Collider 2D中Geometry Type为Polygons。
13.自动添加必需的组件添加可以使用:
[RequireComponent(typeof(Rigidbody2D))]
14.cinemachine中的confiner2D每次获取都需要清理缓存(InvalidateCache)
15.多设备的管理需要使用InputSystem库中的方法进行操作。
16.管理者一般只有一个,被管理者有多个。
17.调用不同物体的相同接口执行不同方法,接口中的方法不需要实现。
18.使用SO写的事件,可以使用激活UnityEvent的方式来呼叫执行事件。
19.攻击执行逻辑
1)Attack(攻击者)
public class Attack : MonoBehaviour
{public int damage;public float attackRange;public float attackRate;//攻击频率private void OnTriggerStay2D(Collider2D other){other.GetComponent<Character>()?.TakeDamage(this);}
}
2)Character(被攻击者)
public void TakeDamage(Attack attacker)
{if (currentHealth - attacker.damage > 0){currentHealth -= attacker.damage;}else{currentHealth = 0;}
}
具体逻辑:实际执行血量减少逻辑实在被攻击者中,攻击者主要起到通知的作用。
20.数据保存逻辑
1)保存接口(需要保存数据的代码调用接口)
public interface ISaveable
{DataDefination GetDataID();void RegisterSaveData(){DataManager.instance.RigisterSaveData(this);} void UnRegisterSaveData() => DataManager.instance.UnRegisterSaveData(this);void GetSaveData(Data data);void LoadData(Data data);
}
2) 数据管理者
public class DataManager : MonoBehaviour
{public static DataManager instance;[Header("事件监听")]public VoidEventSO saveDataEvent;public VoidEventSO loadDataEvent;private List<ISaveable> saveableList = new ();private Data saveData;private void Awake(){if(instance == null)instance = this;elseDestroy(gameObject);saveData = new Data();}private void Update(){if (Keyboard.current.lKey.wasPressedThisFrame){Load();}}private void OnEnable(){saveDataEvent.OnEventRaised += Save;loadDataEvent.OnEventRaised += Load;}private void OnDisable(){saveDataEvent.OnEventRaised -= Save;loadDataEvent.OnEventRaised -= Load;}public void RigisterSaveData(ISaveable saveable){if (!saveableList.Contains(saveable)){saveableList.Add(saveable);}}public void UnRegisterSaveData(ISaveable saveable){saveableList.Remove(saveable);}public void Save(){foreach (var saveable in saveableList){saveable.GetSaveData(saveData);}foreach (var item in saveData.characterPosDict){Debug.Log(item.Key + " : " + item.Value);}}public void Load(){foreach (var saveable in saveableList){saveable.LoadData(saveData);}}
}
3) 数据唯一识别ID
public class DataDefination : MonoBehaviour
{public PersistentType persistentType;public string ID;private void OnValidate(){if (persistentType == PersistentType.ReadWrite){if(ID == string.Empty)ID = System.Guid.NewGuid().ToString();}else{ID = string.Empty;}}
}
4)数据
public class Data
{public string sceneToSave;public Dictionary<string,Vector3> characterPosDict = new ();public Dictionary<string,float> floatSaveData = new ();public GameSceneSO GetSaveScene(){var newScene = ScriptableObject.CreateInstance<GameSceneSO>();return newScene;}
}
具体逻辑:一个脚本需要数据保存与读取,需要调用接口,实现里面的方法并且注册(卸载)注
册表,然后管理者在数据保存和加载时一次调用注册表里的对象执行每个里面对应的
保存与加载方法。
数据会存入Data的每个字典中,每个字典分别由ID与存储数据构成,那么每个脚本挂
载的物体都需要生成唯一可识别ID。
21.Addressable的异步加载
1.场景切换管理
public Transform playerTrans;public Vector3 firstPosition;public Vector3 menuPosition;[Header("场景")]public GameSceneSO firstLoadScene;public GameSceneSO menuScene;[Header("事件广播")]public VoidEventSO afterSceneLoadedEvent;public FadeEventSO fadeEvent;public SceneLoadEventSO unLoadedEvent;public GameSceneSO currentloadScene;private GameSceneSO sceneToLoad;private Vector3 posToGo;private bool isLoading;private void OnBackToMenuEvent(){sceneToLoad = menuScene;loadEventSO.RaiseLoadRequestEvent(sceneToLoad,menuPosition,true);}private void NewGame(){sceneToLoad = firstLoadScene;loadEventSO.RaiseLoadRequestEvent(sceneToLoad,firstPosition,true);}//加载场景前置工作private void OnLoadRequestEvent(GameSceneSO locationToGo, Vector3 posToGo, bool fadeScreen){if(isLoading)return;isLoading = true;sceneToLoad = locationToGo;this.posToGo = posToGo;this.fadeScreen = fadeScreen;if(currentloadScene != null)StartCoroutine(UnLoadPreviousScene());elseLoadNewScene();}//卸载之前场景private IEnumerator UnLoadPreviousScene(){unLoadedEvent.LoadRequestEvent(sceneToLoad, posToGo, fadeScreen);//等待当前场景完全卸载yield return currentloadScene.sceneReference.UnLoadScene();LoadNewScene();}//加载新的场景private void LoadNewScene(){//加载场景并且场景为激活状态var loadingOption = sceneToLoad.sceneReference.LoadSceneAsync(LoadSceneMode.Additive);loadingOption.Completed += OnLoadComplete;}//加载完成之后执行任务private void OnLoadComplete(AsyncOperationHandle<SceneInstance> obj){currentloadScene = sceneToLoad;playerTrans.position = posToGo;isLoading = false;if(currentloadScene.sceneType != SceneType.Menu)afterSceneLoadedEvent.RaiseEvent();}
2.场景SO
[CreateAssetMenu(menuName = "Game Scene/GameSceneSO", fileName = "GameSceneSO")]
public class GameSceneSO : ScriptableObject
{public SceneType sceneType;public AssetReference sceneReference;
}
3.每个场景都需要勾选Addressable,添加进入Addressable Groups里面。
22.可交互逻辑
1.交互接口
public interface IInteractable
{void TriggerAction();
}
2.可交互逻辑
public class Chest : MonoBehaviour,IInteractable
{public void TriggerAction(){Debug.Log("Open Chest!");}
}
3.玩家交互逻辑
public class Sign : MonoBehaviour
{private IInteractable targetItem;private bool canPress;private void OnTriggerStay2D(Collider2D other){if (other.CompareTag("Interactable")){targetItem = other.GetComponent<IInteractable>();canPress = true;}}private void OnTriggerExit2D(Collider2D other){canPress = false;}private void OnConfirm(InputAction.CallbackContext obj){if (canPress && targetItem != null){targetItem.TriggerAction();}}
}
注:获取可交互物体对应的接口,然后执行接口函数,就会执行不同物体的不同方法。