Unity Demo——3D平臺跳躍游戲筆記

今天是一個3D平臺跳躍游戲的筆記。

我們按照以下分類來對這個項目的代碼進行學習:

核心游戲系統 (Core Game Systems)

核心游戲系統是IkunOdyssey項目的基礎,負責所有游戲對象(如玩家、敵人、道具等)的通用行為和物理交互。它通過實體基礎系統和游戲管理系統,為整個游戲提供了統一的物理、碰撞、狀態管理和全局數據控制等功能,保證了游戲世界的有序運行和各模塊的高效協作。

實體基礎系統

實體基礎系統為玩家、敵人等所有游戲對象提供統一的物理運動、碰撞檢測和狀態管理能力,是實現角色行為和交互的底層支撐。

物理移動系統

public virtual void Accelerate(Vector3 direction, float turningDrag, float acceleration, float topSpeed)
{if (direction.sqrMagnitude > 0){var speed = Vector3.Dot(direction, lateralVelocity);var velocity = direction * speed;var turningVelocity = lateralVelocity - velocity;var turningDelta = turningDrag * turningDragMultiplier * Time.deltaTime;var targetTopSpeed = topSpeed * topSpeedMultiplier;if (lateralVelocity.magnitude < targetTopSpeed || speed < 0){speed += acceleration * accelerationMultiplier * Time.deltaTime;speed = Mathf.Clamp(speed, -targetTopSpeed, targetTopSpeed);}velocity = direction * speed;turningVelocity = Vector3.MoveTowards(turningVelocity, Vector3.zero, turningDelta);lateralVelocity = velocity + turningVelocity;}
}public virtual void Decelerate(float deceleration)
{var delta = deceleration * decelerationMultiplier * Time.deltaTime;lateralVelocity = Vector3.MoveTowards(lateralVelocity, Vector3.zero, delta);
}public virtual void Gravity(float gravity)
{if (!isGrounded){verticalVelocity += Vector3.down * gravity * gravityMultiplier * Time.deltaTime;}
}public virtual void SlopeFactor(float upwardForce, float downwardForce)
{if (!isGrounded || !OnSlopingGround()) return;var factor = Vector3.Dot(Vector3.up, groundNormal);var downwards = Vector3.Dot(localSlopeDirection, lateralVelocity) > 0;var multiplier = downwards ? downwardForce : upwardForce;var delta = factor * multiplier * Time.deltaTime;lateralVelocity += localSlopeDirection * delta;
}

這部分代碼實現了實體的物理移動,包括加速、減速、重力和斜坡處理。Accelerate方法讓實體根據輸入方向平滑加速并支持轉向,Decelerate方法讓實體速度逐漸歸零,Gravity方法在實體不在地面時持續施加重力,SlopeFactor方法則根據實體是否在斜坡上調整其速度,模擬上坡減速和下坡加速的效果。這些方法保證了實體在不同地形和狀態下都能有自然的物理表現。?

碰撞檢測系統

public virtual bool CapsuleCast(Vector3 direction, float distance, int layer = Physics.DefaultRaycastLayers,QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.Ignore)
{return CapsuleCast(direction, distance, out _, layer, queryTriggerInteraction);
}public virtual bool CapsuleCast(Vector3 direction, float distance,out RaycastHit hit, int layer = Physics.DefaultRaycastLayers,QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.Ignore)
{var origin = position - direction * radius + center;var offset = transform.up * (height * 0.5f - radius);var top = origin + offset;var bottom = origin - offset;return Physics.CapsuleCast(top, bottom, radius, direction,out hit, distance + radius, layer, queryTriggerInteraction);
}public virtual bool SphereCast(Vector3 direction, float distance, int layer = Physics.DefaultRaycastLayers,QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.Ignore)
{return SphereCast(direction, distance, out _, layer, queryTriggerInteraction);
}public virtual bool SphereCast(Vector3 direction, float distance,out RaycastHit hit, int layer = Physics.DefaultRaycastLayers,QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.Ignore)
{var castDistance = Mathf.Abs(distance - radius);return Physics.SphereCast(position, radius, direction,out hit, castDistance, layer, queryTriggerInteraction);
}public virtual int OverlapEntity(Collider[] result, float skinOffset = 0)
{var contactOffset = skinOffset + controller.skinWidth + Physics.defaultContactOffset;var overlapsRadius = radius + contactOffset;var offset = (height + contactOffset) * 0.5f - overlapsRadius;var top = position + Vector3.up * offset;var bottom = position + Vector3.down * offset;return Physics.OverlapCapsuleNonAlloc(top, bottom, overlapsRadius, result);
}

實現了多種碰撞檢測方式,包括膠囊體射線檢測(CapsuleCast)、球體射線檢測(SphereCast)和實體周圍的重疊檢測(OverlapEntity)。這些方法可以分別用于角色控制器的前方障礙檢測、地面檢測以及判斷實體是否與其他物體發生接觸,從而保證實體在游戲世界中能夠正確地移動、落地和響應碰撞。

狀態管理系統?

public virtual void Change<TState>() where TState : EntityState<T>
{var type = typeof(TState);if (m_states.ContainsKey(type)){Change(m_states[type]);}
}public virtual void Change(EntityState<T> to)
{if (to != null && Time.timeScale > 0){if (current != null){current.Exit(entity);events.onExit.Invoke(current.GetType());last = current;}current = to;current.Enter(entity);events.onEnter.Invoke(current.GetType());events.onChange?.Invoke();}
}public virtual void Step()
{if (current != null && Time.timeScale > 0){current.Step(entity);}
}

實現了實體的狀態管理功能。Change<TState>方法可以通過類型切換到指定的狀態,Change(EntityState<T>)方法則切換到具體的狀態實例,并自動調用狀態的退出和進入方法,同時觸發相關事件。Step方法則在每一幀調用當前狀態的Step邏輯,實現狀態的持續行為處理。通過這種方式,實體可以靈活地在不同狀態間切換,實現如移動、跳躍、受傷等復雜行為的管理。?

游戲管理

游戲管理系統負責全局數據的統一管理、關卡進度控制和存檔功能,是游戲流程和數據安全的核心保障。

游戲主控制器

public class Game : Singleton<Game>
{public UnityEvent<int> OnRetriesSet;public UnityEvent OnSavingRequested;public int initialRetries = 3;public List<GameLevel> levels;protected int m_retries;protected int m_dataIndex;protected DateTime m_createdAt;protected DateTime m_updatedAt;public int retries{get { return m_retries; }set{m_retries = value;OnRetriesSet?.Invoke(m_retries);}}public static void LockCursor(bool value = true){
#if UNITY_STANDALONE || UNITY_WEBGLCursor.visible = value;Cursor.lockState = value ? CursorLockMode.Locked : CursorLockMode.None;
#endif}public virtual void LoadState(int index, GameData data){m_dataIndex = index;m_retries = data.retries;m_createdAt = DateTime.Parse(data.createdAt);m_updatedAt = DateTime.Parse(data.updatedAt);for (int i = 0; i < data.levels.Length; i++){levels[i].LoadState(data.levels[i]);}}public virtual LevelData[] LevelsData(){return levels.Select(level => level.ToData()).ToArray();}public virtual GameLevel GetCurrentLevel(){var scene = GameLoader.instance.currentScene;return levels.Find((level) => level.scene == scene);}public virtual int GetCurrentLevelIndex(){var scene = GameLoader.instance.currentScene;return levels.FindIndex((level) => level.scene == scene);}public virtual void RequestSaving(){GameSaver.instance.Save(ToData(), m_dataIndex);OnSavingRequested?.Invoke();}public virtual void UnlockLevelBySceneName(string sceneName){var level = levels.Find((level) => level.scene == sceneName);if (level != null){level.locked = false;}}public virtual void UnlockNextLevel(){var index = GetCurrentLevelIndex() + 1;if (index >= 0 && index < levels.Count){levels[index].locked = false;}}public virtual GameData ToData(){return new GameData(){retries = m_retries,levels = LevelsData(),createdAt = m_createdAt.ToString(),updatedAt = DateTime.UtcNow.ToString()};}protected override void Awake(){base.Awake();retries = initialRetries;DontDestroyOnLoad(gameObject);}
}

實現了游戲的全局管理功能。Game類采用單例模式,負責管理重試次數、關卡列表、數據加載與保存、關卡解鎖等全局邏輯。它還通過事件機制與UI等其他系統解耦,保證了游戲流程的統一和數據的持久化。Awake方法確保Game對象在場景切換時不會被銷毀,保證了全局數據的持續有效。

游戲數據管理?

[Serializable]
public class GameData
{public int retries;public LevelData[] levels;public string createdAt;public string updatedAt;public static GameData Create(){return new GameData(){retries = Game.instance.initialRetries,createdAt = DateTime.UtcNow.ToString(),updatedAt = DateTime.UtcNow.ToString(),levels = Game.instance.levels.Select((level) =>{return new LevelData(){locked = level.locked};}).ToArray()};}public virtual int TotalStars(){return levels.Aggregate(0, (acc, level) =>{var total = level.CollectedStars();return acc + total;});}public virtual int TotalCoins(){return levels.Aggregate(0, (acc, level) => acc + level.coins);}public virtual string ToJson(){return JsonUtility.ToJson(this);}public static GameData FromJson(string json){return JsonUtility.FromJson<GameData>(json);}
}

定義了游戲的數據結構和序列化方法。GameData類用于存儲玩家的重試次數、關卡進度、創建和更新時間等信息,并提供了JSON序列化和反序列化的方法,方便數據的保存和讀取。它還支持統計所有關卡的星星和金幣總數,便于全局進度統計和成就系統的實現。

存檔系統?

public class GameSaver : Singleton<GameSaver>
{public enum Mode { Binary, JSON, PlayerPrefs }public Mode mode = Mode.Binary;public string fileName = "save";public string binaryFileExtension = "data";protected static readonly int TotalSlots = 5;public virtual void Save(GameData data, int index){switch (mode){default:case Mode.Binary:SaveBinary(data, index);break;case Mode.JSON:SaveJSON(data, index);break;case Mode.PlayerPrefs:SavePlayerPrefs(data, index);break;}}public virtual GameData Load(int index){switch (mode){default:case Mode.Binary:return LoadBinary(index);case Mode.JSON:return LoadJSON(index);case Mode.PlayerPrefs:return LoadPlayerPrefs(index);}}public virtual void Delete(int index){switch (mode){default:case Mode.Binary:case Mode.JSON:DeleteFile(index);break;case Mode.PlayerPrefs:DeletePlayerPrefs(index);break;}}public virtual GameData[] LoadList(){var list = new GameData[TotalSlots];for (int i = 0; i < TotalSlots; i++){var data = Load(i);if (data != null){list[i] = data;}}return list;}// ... 省略具體的文件操作方法
}

實現了多格式的存檔系統,支持二進制、JSON和PlayerPrefs三種存檔方式,并支持多存檔槽位。Save、Load、Delete等方法根據當前模式選擇不同的存儲方式,LoadList方法可以一次性加載所有存檔槽的數據,方便實現多存檔和存檔管理功能。這樣可以保證玩家的游戲進度能夠安全、靈活地保存和恢復。

玩家系統?(Player System)

玩家系統是游戲中最核心的交互模塊,負責玩家角色的輸入響應、屬性管理、動畫音效、狀態切換等一系列行為控制,確保玩家在游戲世界中的一切操作都能被準確、流暢地反饋和表現出來。

玩家核心控制

玩家核心控制模塊負責玩家角色的整體行為調度,包括輸入管理、屬性管理、生命值、狀態切換等,是所有玩家行為的總入口。

玩家主控制器

public class Player : Entity<Player>
{public PlayerEvents playerEvents;public Transform pickableSlot;public Transform skin;public PlayerInputManager inputs { get; protected set; }public PlayerStatsManager stats { get; protected set; }public Health health { get; protected set; }public bool onWater { get; protected set; }public bool holding { get; protected set; }public int jumpCounter { get; protected set; }public int airSpinCounter { get; protected set; }public int airDashCounter { get; protected set; }public float lastDashTime { get; protected set; }public Vector3 lastWallNormal { get; protected set; }public Pole pole { get; protected set; }public Collider water { get; protected set; }public Pickable pickable { get; protected set; }public virtual bool isAlive => !health.isEmpty;public virtual bool canStandUp => !SphereCast(Vector3.up, originalHeight);protected virtual void InitializeInputs() => inputs = GetComponent<PlayerInputManager>();protected virtual void InitializeStats() => stats = GetComponent<PlayerStatsManager>();protected virtual void InitializeHealth() => health = GetComponent<Health>();protected virtual void InitializeTag() => tag = GameTags.Player;protected override void Awake(){base.Awake();InitializeInputs();InitializeStats();InitializeHealth();InitializeTag();}
}

這段代碼定義了玩家的主控制器Player類,繼承自實體基礎系統。它聚合了輸入管理、屬性管理、生命值、事件等多個組件,并在Awake階段完成初始化。通過這些成員變量和初始化方法,玩家對象能夠統一管理自身的輸入、屬性、生命狀態和標簽,成為所有玩家行為的核心調度中心。?

玩家輸入管理?

public class PlayerInputManager : MonoBehaviour
{public Vector2 moveInput { get; private set; }public bool jumpPressed { get; private set; }public bool dashPressed { get; private set; }// ... 其他輸入狀態void Update(){moveInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));jumpPressed = Input.GetButtonDown("Jump");dashPressed = Input.GetButtonDown("Dash");// ... 其他輸入檢測}
}

實現了玩家輸入的采集和管理。PlayerInputManager會在每一幀讀取鍵盤或手柄的輸入狀態,并將結果存儲在對應的屬性中,供玩家主控制器和狀態機隨時讀取。這樣可以實現輸入與行為的解耦,方便后續擴展和多平臺適配。

玩家屬性管理

[CreateAssetMenu(fileName = "NewPlayerStats", menuName = "PLAYER TWO/Platformer Project/Player/New Player Stats")]
public class PlayerStats : EntityStats<PlayerStats>
{public float acceleration = 13f;public float deceleration = 28f;public float topSpeed = 6f;public int multiJumps = 1;public float maxJumpHeight = 17f;public float minJumpHeight = 10f;public float dashForce = 25f;public float dashDuration = 0.3f;// ... 其他屬性
}

?定義了玩家的屬性配置,采用ScriptableObject方式,便于在Unity編輯器中可視化調整。屬性包括加速度、最大速度、跳躍高度、沖刺參數等,所有與玩家能力相關的數值都集中在這里統一管理,方便平衡和調優。

玩家狀態管理

public class PlayerStateManager : EntityStateManager<Player>
{protected override List<EntityState<Player>> GetStateList(){return new List<EntityState<Player>>(){new IdlePlayerState(),new MovePlayerState(),new JumpPlayerState(),new SwimPlayerState(),// ... 其他狀態};}
}

實現了玩家的狀態管理器。通過重寫GetStateList方法,將所有玩家可能的狀態(如Idle、Move、Jump、Swim等)注冊到狀態機中。這樣,玩家可以根據輸入和環境在不同狀態間切換,實現復雜的行為邏輯和動畫切換。?

玩家功能模塊

玩家功能模塊包括動畫、音效、粒子、相機等,負責將玩家的行為以視覺和聽覺的方式反饋給玩家,提升游戲體驗。

玩家動畫控制

public class PlayerAnimator : MonoBehaviour
{public Animator animator;public void SetMoveSpeed(float speed){animator.SetFloat("MoveSpeed", speed);}public void SetJump(bool isJumping){animator.SetBool("IsJumping", isJumping);}// ... 其他動畫參數設置
}

通過Animator組件控制玩家的動畫狀態。根據玩家的移動速度、跳躍等行為,動態設置動畫參數,實現角色動作與實際操作的同步。

玩家音效與粒子?

public class PlayerAudio : MonoBehaviour
{public AudioSource audioSource;public AudioClip jumpClip;public AudioClip dashClip;public void PlayJump(){audioSource.PlayOneShot(jumpClip);}public void PlayDash(){audioSource.PlayOneShot(dashClip);}
}

負責播放玩家的音效反饋。每當玩家執行跳躍、沖刺等動作時,調用對應方法即可播放相應音效,增強操作的打擊感和沉浸感。?

敵人系統 (Enemy?System)

敵人系統負責游戲中所有敵方單位的行為邏輯、屬性管理、AI感知、攻擊判定、動畫音效等。它不僅讓敵人具備基礎的移動、攻擊、受傷、死亡等能力,還支持復雜的AI狀態切換、路徑巡邏、與玩家互動等高級功能,是游戲挑戰性和趣味性的關鍵來源。

敵人核心控制

敵人核心控制模塊負責敵人對象的整體行為調度,包括屬性管理、狀態切換、AI感知、攻擊判定等,是所有敵人行為的總入口。

public class Enemy : Entity<Enemy>
{public EnemyEvents enemyEvents;protected Player m_player;public EnemyStatsManager stats { get; protected set; }public WaypointManager waypoints { get; protected set; }public Health health { get; protected set; }public Player player { get; protected set; }protected virtual void InitializeStatsManager() => stats = GetComponent<EnemyStatsManager>();protected virtual void InitializeWaypointsManager() => waypoints = GetComponent<WaypointManager>();protected virtual void InitializeHealth() => health = GetComponent<Health>();protected virtual void InitializeTag() => tag = GameTags.Enemy;protected override void Awake(){base.Awake();InitializeTag();InitializeStatsManager();InitializeWaypointsManager();InitializeHealth();}public override void ApplyDamage(int amount, Vector3 origin){if (!health.isEmpty && !health.recovering){health.Damage(amount);enemyEvents.OnDamage?.Invoke();if (health.isEmpty){controller.enabled = false;enemyEvents.OnDie?.Invoke();}}}public virtual void Revive(){if (!health.isEmpty) return;health.Reset();controller.enabled = true;enemyEvents.OnRevive.Invoke();}protected override void OnUpdate(){HandleSight();ContactAttack();}
}

定義了敵人的主控制器Enemy類,繼承自實體基礎系統。它聚合了屬性管理、路徑點管理、生命值、事件等多個組件,并在Awake階段完成初始化。ApplyDamage方法處理敵人受傷和死亡,Revive方法實現復活,OnUpdate方法則每幀處理AI感知和攻擊判定。通過這些成員變量和方法,敵人對象能夠統一管理自身的屬性、生命、AI和與玩家的互動。

敵人屬性與狀態管理

敵人屬性配置

[CreateAssetMenu(fileName = "NewEnemyStats", menuName = "PLAYER TWO/Platformer Project/Enemy/New Enemy Stats")]
public class EnemyStats : EntityStats<EnemyStats>
{public float acceleration = 10f;public float deceleration = 20f;public float topSpeed = 4f;public float gravity = 30f;public float rotationSpeed = 600f;public bool canAttackOnContact = true;public int contactDamage = 1;public float contactPushBackForce = 5f;public float spotRange = 10f;public float viewRange = 15f;// ... 其他屬性
}

這段代碼定義了敵人的屬性配置,采用ScriptableObject方式,便于在Unity編輯器中可視化調整。屬性包括加速度、最大速度、重力、攻擊參數、感知范圍等,所有與敵人能力相關的數值都集中在這里統一管理,方便平衡和調優。

敵人屬性管理器?

public class EnemyStatsManager : EntityStatsManager<EnemyStats> {}

通過泛型繼承,直接復用實體屬性管理器的功能,實現對EnemyStats的統一管理。

敵人狀態管理?

public class EnemyStateManager : EntityStateManager<Enemy>
{protected override List<EntityState<Enemy>> GetStateList(){return new List<EntityState<Enemy>>(){new IdleEnemyState(),new PatrolEnemyState(),new ChaseEnemyState(),new AttackEnemyState(),new HurtEnemyState(),new DieEnemyState(),// ... 其他狀態};}
}

通過重寫GetStateList方法,將所有敵人可能的狀態(如Idle、Patrol、Chase、Attack、Hurt、Die等)注冊到狀態機中。這樣,敵人可以根據AI邏輯和環境在不同狀態間切換,實現復雜的行為邏輯和動畫切換。?

敵人AI與感知系統

視野與感知

protected virtual void HandleSight()
{if (!player){var overlaps = Physics.OverlapSphereNonAlloc(position, stats.current.spotRange, m_sightOverlaps);for (int i = 0; i < overlaps; i++){if (m_sightOverlaps[i].CompareTag(GameTags.Player)){if (m_sightOverlaps[i].TryGetComponent<Player>(out var player)){this.player = player;enemyEvents.OnPlayerSpotted?.Invoke();return;}}}}else{var distance = Vector3.Distance(position, player.position);if ((player.health.current == 0) || (distance > stats.current.viewRange)){player = null;enemyEvents.OnPlayerScaped?.Invoke();}}
}

實現了敵人的視野感知功能。HandleSight方法會在每一幀檢測周圍是否有玩家進入感知范圍,如果發現玩家則觸發“發現玩家”事件;如果玩家離開視野或死亡,則觸發“玩家逃脫”事件。這樣可以實現敵人對玩家的動態感知和追蹤。

路徑點與巡邏?

public class WaypointManager : MonoBehaviour
{public Transform[] waypoints;public int currentIndex = 0;public Transform GetCurrentWaypoint(){if (waypoints.Length == 0) return null;return waypoints[currentIndex];}public void MoveToNextWaypoint(){if (waypoints.Length == 0) return;currentIndex = (currentIndex + 1) % waypoints.Length;}
}

實現了敵人的路徑點巡邏功能。WaypointManager管理一組巡邏點,敵人可以依次移動到每個點,實現循環巡邏。通過GetCurrentWaypoint和MoveToNextWaypoint方法,敵人AI可以靈活地控制巡邏路線。?

敵人攻擊與交互

public virtual void ContactAttack()
{if (stats.current.canAttackOnContact){var overlaps = OverlapEntity(m_contactAttackOverlaps, stats.current.contactOffset);for (int i = 0; i < overlaps; i++){if (m_contactAttackOverlaps[i].CompareTag(GameTags.Player) &&m_contactAttackOverlaps[i].TryGetComponent<Player>(out var player)){var stepping = controller.bounds.max + Vector3.down * stats.current.contactSteppingTolerance;if (!player.IsPointUnderStep(stepping)){if (stats.current.contactPushback){lateralVelocity = -transform.forward * stats.current.contactPushBackForce;}player.ApplyDamage(stats.current.contactDamage, transform.position);enemyEvents.OnPlayerContact?.Invoke();}}}}
}

實現了敵人的接觸攻擊判定,ContactAttack方法會檢測敵人周圍是否有玩家,如果有則對玩家造成傷害,并根據配置決定是否將敵人自身擊退。這樣可以實現敵人與玩家的近戰碰撞和傷害判定。?

敵人動畫與音效

動畫控制

public class EnemyAnimator : MonoBehaviour
{public Animator animator;public void SetMoveSpeed(float speed){animator.SetFloat("MoveSpeed", speed);}public void SetAttack(bool isAttacking){animator.SetBool("IsAttacking", isAttacking);}// ... 其他動畫參數設置
}

通過Animator組件控制敵人的動畫狀態。根據敵人的移動速度、攻擊等行為,動態設置動畫參數,實現角色動作與實際AI行為的同步。

音效反饋?

public class EnemyAudio : MonoBehaviour
{public AudioSource audioSource;public AudioClip attackClip;public AudioClip hurtClip;public void PlayAttack(){audioSource.PlayOneShot(attackClip);}public void PlayHurt(){audioSource.PlayOneShot(hurtClip);}
}

播放敵人的音效反饋,每當敵人執行攻擊、受傷等動作時,調用對應方法即可播放相應音效,增強敵人的存在感和打擊感。?

敵人事件系統

[Serializable]
public class EnemyEvents
{public UnityEvent OnDamage;public UnityEvent OnDie;public UnityEvent OnRevive;public UnityEvent OnPlayerSpotted;public UnityEvent OnPlayerScaped;public UnityEvent OnPlayerContact;
}

定義了敵人的事件系統,通過UnityEvent,敵人可以在受傷、死亡、復活、發現玩家、玩家逃脫、接觸玩家等關鍵時刻觸發事件,方便與UI、音效、特效等其他系統解耦聯動。?

關卡系統 (Level?System)

關卡系統負責游戲中每一關的流程控制、數據管理、評分統計、重生與完成判定等,是游戲內容組織、進度推進和挑戰反饋的核心模塊。它不僅管理關卡的啟動、暫停、重生、完成等流程,還負責關卡內的分數、星星、金幣等收集與統計,確保玩家的游戲體驗連貫且富有成就感。

關卡核心管理

關卡核心管理模塊負責關卡的整體生命周期,包括關卡的初始化、暫停、重生、完成等,是所有關卡行為的總入口。

關卡主控制器

public class GameLevel : MonoBehaviour
{public string scene;public bool locked = true;public float time;public int coins;public bool[] stars = new bool[3];public void LoadState(LevelData data){locked = data.locked;time = data.time;coins = data.coins;stars = (bool[])data.stars.Clone();}public LevelData ToData(){return new LevelData{locked = this.locked,time = this.time,coins = this.coins,stars = (bool[])this.stars.Clone()};}
}

定義了關卡的主控制器GameLevel類,負責記錄關卡的場景名、解鎖狀態、最佳時間、金幣數和星星收集情況。LoadState和ToData方法用于關卡數據的加載和保存,方便與全局存檔系統對接,實現關卡進度的持久化。

關卡數據結構?

[Serializable]
public class LevelData
{public bool locked;public float time;public int coins;public bool[] stars = new bool[3];public int CollectedStars(){int count = 0;foreach (var s in stars)if (s) count++;return count;}
}

定義了關卡的數據結構LevelData,包含解鎖狀態、最佳時間、金幣數和星星收集情況,并提供了統計已收集星星數量的方法。該結構體用于關卡數據的存檔、加載和統計。?

關卡流程控制

關卡啟動與暫停

public class LevelStarter : MonoBehaviour
{void Start(){// 初始化關卡相關內容// 比如重置分數、計時、玩家位置等}
}public class LevelPauser : MonoBehaviour
{public void Pause(){Time.timeScale = 0;}public void Resume(){Time.timeScale = 1;}
}

實現了關卡的啟動和暫停功能,LevelStarter在關卡開始時初始化相關內容,LevelPauser則通過修改Time.timeScale來實現暫停和恢復,保證玩家可以隨時中斷和繼續游戲。?

關卡重生與完成

public class LevelRespawner : MonoBehaviour
{public Transform respawnPoint;public Player player;public void Respawn(){player.transform.position = respawnPoint.position;player.Respawn();}
}public class LevelFinisher : MonoBehaviour
{public void FinishLevel(){// 統計分數、保存進度、切換場景等LevelScore.instance.Consolidate();Game.instance.UnlockNextLevel();// ... 其他完成關卡的邏輯}
}

實現了關卡的重生和完成判定,LevelRespawner負責將玩家重置到重生點并恢復狀態,LevelFinisher則在關卡完成時統計分數、保存進度并解鎖下一關,確保關卡流程的完整性和連貫性。?

關卡評分與統計

public class LevelScore : Singleton<LevelScore>
{public UnityEvent<int> OnCoinsSet;public UnityEvent<bool[]> OnStarsSet;public UnityEvent OnScoreLoaded;public int coins { get; set; }public bool[] stars { get; private set; }public float time { get; private set; }public bool stopTime { get; set; } = true;public virtual void Reset(){time = 0;coins = 0;// stars初始化}public virtual void CollectStar(int index){stars[index] = true;OnStarsSet?.Invoke(stars);}public virtual void Consolidate(){// 更新關卡最佳成績、保存數據// ...Game.instance.RequestSaving();}void Update(){if (!stopTime){time += Time.deltaTime;}}
}

這段代碼實現了關卡的評分系統。LevelScore負責記錄和統計關卡內的金幣、星星、用時等數據,并通過事件通知UI等系統更新顯示。CollectStar方法用于收集星星,Consolidate方法用于關卡完成時保存成績和進度,Update方法則負責計時。

關卡UI與反饋

public class HUD : MonoBehaviour
{public Text retries;public Text coins;public Text health;public Text timer;public Image[] starsImages;protected Game m_game;protected LevelScore m_score;protected Player m_player;public virtual void Refresh(){// 刷新UI顯示coins.text = m_score.coins.ToString("000");retries.text = m_game.retries.ToString("00");health.text = m_player.health.current.ToString("0");// 星星和計時等}void Awake(){m_game = Game.instance;m_score = LevelScore.instance;m_player = FindObjectOfType<Player>();// 監聽事件,自動刷新}void Update(){// 實時更新計時器}
}

實現了關卡內的HUD顯示,HUD負責將金幣、重試次數、生命值、計時器、星星等信息實時顯示在屏幕上,并通過事件機制與評分系統、游戲管理系統聯動,保證UI信息的實時性和準確性。

關卡事件與數據交互?

關卡系統通過GameLevel、LevelData、LevelScore等類與全局的GameData、Game等系統進行數據交互,實現關卡進度的保存、加載、統計和解鎖。每當關卡完成、分數更新、星星收集等事件發生時,都會通過Consolidate、ToData等方法將數據同步到全局存檔,保證玩家的游戲進度不會丟失。

Consolidate(合并/整合)方法用于在關卡完成時,將本次游戲過程中獲得的分數、星星、用時等數據,合并到當前關卡的GameLevel對象中,并觸發全局存檔。它確保如果玩家本次表現比歷史最好成績更好,就會更新關卡的最佳記錄。

public virtual void Consolidate()
{if (m_level != null){if (m_level.time == 0 || time < m_level.time){m_level.time = time;}if (coins > m_level.coins){m_level.coins = coins;}m_level.stars = (bool[])stars.Clone();m_game.RequestSaving();}
}
  • 如果本次用時比歷史最佳更短,則更新最佳用時。
  • 如果本次金幣數比歷史更多,則更新金幣數。
  • 星星收集情況也會被更新。
  • 最后調用m_game.RequestSaving(),將最新數據保存到全局存檔。

ToData方法的作用是將當前對象(如關卡、全局游戲)的運行時狀態,轉換為可序列化的數據結構(如LevelData、GameData),以便進行存檔、網絡傳輸或數據統計。

public LevelData ToData()
{return new LevelData{locked = this.locked,time = this.time,coins = this.coins,stars = (bool[])this.stars.Clone()};
}

?將GameLevel對象的狀態(解鎖、時間、金幣、星星)轉換為LevelData結構,便于存檔和數據傳遞。

用戶界面系統?(UI?System)

UI系統負責游戲中所有用戶界面元素的顯示與交互,包括游戲內HUD、關卡選擇、存檔管理、動畫反饋等。它不僅為玩家提供實時的游戲信息(如分數、生命、星星、計時等),還承擔著菜單導航、存檔操作、關卡切換等重要功能,是玩家與游戲世界溝通的橋梁。

HUD(游戲內信息顯示)

HUD(Head-Up Display)是游戲過程中最常見的UI,負責實時顯示玩家的分數、生命、金幣、星星、計時等關鍵信息。

public class HUD : MonoBehaviour
{public string retriesFormat = "00";public string coinsFormat = "000";public string healthFormat = "0";public Text retries;public Text coins;public Text health;public Text timer;public Image[] starsImages;protected Game m_game;protected LevelScore m_score;protected Player m_player;protected float timerStep;protected static float timerRefreshRate = .1f;protected virtual void UpdateCoins(int value){coins.text = value.ToString(coinsFormat);}protected virtual void UpdateRetries(int value){retries.text = value.ToString(retriesFormat);}protected virtual void UpdateHealth(){health.text = m_player.health.current.ToString(healthFormat);}protected virtual void UpdateStars(bool[] value){for (int i = 0; i < starsImages.Length; i++){starsImages[i].enabled = value[i];}}protected virtual void UpdateTimer(){timerStep += Time.deltaTime;if (timerStep >= timerRefreshRate){timer.text = GameLevel.FormattedTime(m_score.time);timerStep = 0;}}public virtual void Refresh(){UpdateCoins(m_score.coins);UpdateRetries(m_game.retries);UpdateHealth();UpdateStars(m_score.stars);}protected virtual void Awake(){m_game = Game.instance;m_score = LevelScore.instance;m_player = FindObjectOfType<Player>();m_score.OnScoreLoaded.AddListener(() =>{m_score.OnCoinsSet.AddListener(UpdateCoins);m_score.OnStarsSet.AddListener(UpdateStars);m_game.OnRetriesSet.AddListener(UpdateRetries);m_player.health.onChange.AddListener(UpdateHealth);Refresh();});}protected virtual void Update() => UpdateTimer();
}

實現了HUD的全部核心功能。它通過監聽分數、星星、重試次數、生命值等事件,實時刷新UI顯示,并在Update中定時刷新計時器。Awake方法中完成了所有依賴對象的獲取和事件綁定,Refresh方法則可手動強制刷新所有UI元素,保證信息的準確性和實時性。?

關卡選擇與存檔管理UI

關卡卡片與關卡列表

public class UILevelCard : MonoBehaviour
{public Text levelName;public Image[] stars;public Button playButton;public void SetData(LevelData data){levelName.text = "Level " + (data.levelIndex + 1);for (int i = 0; i < stars.Length; i++){stars[i].enabled = data.stars != null && i < data.stars.Length && data.stars[i];}playButton.interactable = !data.locked;}
}

實現了關卡選擇界面中每個關卡卡片的顯示。SetData方法根據LevelData設置關卡名稱、星星收集情況和按鈕可用狀態,方便玩家直觀了解每一關的進度和成就。?

存檔卡片與存檔列表

public class UISaveCard : MonoBehaviour
{public Text saveName;public Text progress;public Button loadButton;public Button deleteButton;public void SetData(GameData data, int slotIndex){saveName.text = $"存檔{slotIndex + 1}";progress.text = $"星星:{data.TotalStars()} 金幣:{data.TotalCoins()}";loadButton.interactable = true;deleteButton.interactable = true;}
}

實現了存檔管理界面中每個存檔卡片的顯示。SetData方法根據GameData設置存檔名稱和進度信息,并控制按鈕的可用性,方便玩家管理多個存檔。?

UI動畫與交互輔助

UI動畫控制

public class UIAnimator : MonoBehaviour
{public Animator animator;public void PlayShow(){animator.SetTrigger("Show");}public void PlayHide(){animator.SetTrigger("Hide");}
}

通過Animator組件控制UI的顯示和隱藏動畫。PlayShow和PlayHide方法分別觸發不同的動畫狀態,提升UI的動態表現力和用戶體驗。

UI焦點與自動滾動?

public class UIFocusKeeper : MonoBehaviour
{public Selectable defaultSelectable;void OnEnable(){if (defaultSelectable != null)defaultSelectable.Select();}
}

保證UI界面激活時自動聚焦到指定的按鈕或輸入框,提升鍵盤/手柄操作的友好性。?

事件系統 (Event?System)

事件系統是游戲各個模塊之間解耦通信的橋梁。它通過事件發布與監聽機制,讓玩家、敵人、關卡、UI等系統能夠在關鍵時刻(如受傷、死亡、分數變化、關卡完成等)互相通知和響應,極大提升了代碼的靈活性、可維護性和擴展性。

事件定義與基礎結構

[Serializable]
public class PlayerEvent : UnityEvent<Player> {}

定義了一個帶有Player參數的UnityEvent,方便在玩家相關的事件(如受傷、死亡、復活等)中傳遞玩家對象本身,實現更靈活的事件響應。

[Serializable]
public class EnemyEvents
{public UnityEvent OnDamage;public UnityEvent OnDie;public UnityEvent OnRevive;public UnityEvent OnPlayerSpotted;public UnityEvent OnPlayerScaped;public UnityEvent OnPlayerContact;
}

敵人相關的所有事件,包括受傷、死亡、復活、發現玩家、玩家逃脫、接觸玩家等。每個事件都可以在Inspector中綁定多個響應函數,實現靈活的事件驅動。

[Serializable]
public class EntityEvents : UnityEvent {}

?定義了實體通用事件,適用于所有繼承自Entity的對象,便于統一管理和擴展。

事件觸發與監聽

事件觸發

// 以玩家受傷為例(Player.cs)
public override void ApplyDamage(int amount, Vector3 origin)
{if (!health.isEmpty && !health.recovering){health.Damage(amount);playerEvents.OnHurt?.Invoke();if (health.isEmpty){playerEvents.OnDie?.Invoke();}}
}

在玩家受到傷害時,先減少生命值,然后觸發OnHurt事件。如果生命值歸零,則觸發OnDie事件。這樣可以讓UI、音效、動畫等系統及時響應玩家受傷和死亡。

// 以敵人受傷為例(Enemy.cs)
public override void ApplyDamage(int amount, Vector3 origin)
{if (!health.isEmpty && !health.recovering){health.Damage(amount);enemyEvents.OnDamage?.Invoke();if (health.isEmpty){controller.enabled = false;enemyEvents.OnDie?.Invoke();}}
}

?在敵人受到傷害時,先減少生命值,然后觸發OnDamage事件。如果生命值歸零,則觸發OnDie事件。這樣可以讓特效、音效、分數統計等系統及時響應敵人受傷和死亡。

事件監聽

// 以UI監聽玩家生命變化為例(HUD.cs)
protected virtual void Awake()
{m_game = Game.instance;m_score = LevelScore.instance;m_player = FindObjectOfType<Player>();m_player.health.onChange.AddListener(UpdateHealth);// 其他事件監聽...
}

在HUD初始化時,監聽玩家生命值變化事件,每當生命值變化時自動刷新UI顯示,保證信息的實時性。

// 以關卡分數監聽金幣變化為例(LevelScore.cs)
public virtual int coins
{get { return m_coins; }set{m_coins = value;OnCoinsSet?.Invoke(m_coins);}
}

?在金幣數量變化時,自動觸發OnCoinsSet事件,通知所有監聽者(如HUD)及時更新顯示。

工具和輔助系統 (Tools?&?Utilities)

工具與輔助系統為項目開發和運行提供了各種便捷功能,包括類型名稱工具、編輯器擴展、路徑點管理、接口定義等。這些工具類和輔助腳本雖然不直接參與游戲核心玩法,但極大提升了開發效率、代碼復用性和項目的可維護性。

類型名稱工具

public static class ClassTypeName
{public static string Get<T>(){return typeof(T).Name;}
}

實現了一個靜態工具類,用于獲取任意類型的類名字符串。通過泛型方法Get<T>(),可以方便地在日志、調試、反射等場景下獲取類型名稱,提升代碼的可讀性和調試效率。?

編輯器擴展

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;public class ExampleEditorTool
{[MenuItem("Tools/Example/Print Hello")]public static void PrintHello(){Debug.Log("Hello from Editor Tool!");}
}
#endif

這段代碼演示了如何通過Unity編輯器擴展自定義菜單項。開發者可以在Unity菜單欄中添加自定義工具,提升開發效率,比如批量操作、自動化處理等。?

路徑點與導航輔助

public class WaypointManager : MonoBehaviour
{public Transform[] waypoints;public int currentIndex = 0;public Transform GetCurrentWaypoint(){if (waypoints.Length == 0) return null;return waypoints[currentIndex];}public void MoveToNextWaypoint(){if (waypoints.Length == 0) return;currentIndex = (currentIndex + 1) % waypoints.Length;}
}

實現了路徑點管理功能,常用于敵人巡邏、NPC移動等場景。WaypointManager維護一組路徑點,支持獲取當前路徑點和切換到下一個路徑點,方便AI和導航系統的實現。?

接口與事件輔助

public interface IEntityContact
{void OnEntityContact(Entity entity);
}

定義了一個實體接觸接口。實現該接口的組件可以在與其他實體發生接觸時被自動調用,實現如機關觸發、拾取、特殊交互等功能,提升系統的擴展性和靈活性。?

游戲標簽和配置?(Game Configuration)

游戲標簽和配置系統為項目提供了統一的標簽定義、全局常量、Layer/Tag管理以及項目級的參數配置。它不僅方便代碼中對不同類型對象的快速識別和分組,還為后續的物理碰撞、事件判定、關卡管理等提供了基礎支撐,是保證項目規范性和可維護性的關鍵部分。

游戲標簽定義

public static class GameTags
{public const string Player = "Player";public const string Enemy = "Enemy";public const string Platform = "Platform";public const string InteractiveRail = "InteractiveRail";// ... 其他標簽
}

這段代碼定義了一個靜態類GameTags,集中管理了項目中所有用到的Tag字符串常量。這樣做可以避免在代碼中硬編碼字符串,減少拼寫錯誤,提高代碼的可讀性和可維護性。比如在碰撞檢測、查找對象、事件判定等場景下,直接用GameTags.Player等常量即可。?

Layer/Tag?配置與使用

// 檢查碰撞體是否為玩家
if (other.CompareTag(GameTags.Player))
{// 玩家相關邏輯
}

這段代碼展示了如何在實際開發中使用標簽常量進行對象類型判斷。通過CompareTag方法結合GameTags常量,可以高效且安全地判斷對象類型,便于實現不同對象的專屬邏輯。?

項目全局配置

在Unity項目中,ProjectSettings/目錄下包含了諸如TagManager.asset、InputManager.asset、Physics2DSettings.asset等全局配置文件。這些文件通過Unity編輯器進行管理,決定了項目的輸入映射、物理參數、標簽和Layer等全局行為。

示例說明:

  • TagManager.asset:管理所有Tag和Layer的定義,保證代碼和編輯器中的標簽一致。
  • InputManager.asset:配置所有輸入軸和按鍵映射,便于多平臺適配。
  • Physics2DSettings.asset/PhysicsSettings.asset:配置物理引擎參數,如重力、碰撞層、物理步長等。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914165.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914165.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914165.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C語言】回調函數、轉移表、qsort 使用與基于qsort改造冒泡排序

文章目錄數組指針/指針數組函數指針函數指針數組函數指針數組用途(轉移表)回調函數qsort函數基于qsort改造冒泡排序源碼數組指針/指針數組 int arr1[5] { 1,2,3,4,5 };int (*p1)[5] &arr1; //p1是數組指針變量int* arr2[5] { 0 }; //arr2是指針數組指針數組是存放指…

vue3 uniapp 使用ref更新值后子組件沒有更新 ref reactive的區別?使用from from -item執行表單驗證一直提示沒有值

遇到這樣一個問題&#xff0c;我有個1個頁面A&#xff0c;一個from表單組件&#xff0c;一個form-item組件&#xff0c; 使用是這樣的&#xff0c;我在父組件A中使用 &#xff0c;執行表單驗證一直提示沒有值咱們先來講一講ref 和reactive的區別 ref 用來創建一個基本類型或單…

PyQt5布局管理(QBoxLayout(框布局))

QBoxLayout&#xff08;框布局&#xff09; 采用QBoxLayout類可以在水平和垂直方向上排列控件&#xff0c;QHBoxLayout和 QVBoxLayout類繼承自QBoxLayout類。 QHBoxLayout&#xff08;水平布局&#xff09; 采用QHBoxLayout類&#xff0c;按照從左到右的順序來添加控件。QHBoxL…

Grok 4作戰圖刷爆全網,80%華人橫掃硅谷!清華上交校友領銜,95后站C位

來源 | 新智元短短兩年&#xff0c;馬斯克Grok 4的橫空出世&#xff0c;讓xAI團隊一舉站上AI之巔。昨日一小時發布會&#xff0c;Grok 4讓所有人大開眼界&#xff0c;直接刷爆了AIME 2025、人類最后的考試&#xff08;HLE&#xff09;兩大基準。這是狂堆20萬GPU才換來的驚人成果…

AI大模型(七)Langchain核心模塊與實戰(二)

Langchain核心模塊與實戰&#xff08;二&#xff09;Langchian向量數據庫檢索Langchian構建向量數據庫和檢索器批量搜索返回與之相似度最高的第一個檢索器和模型結合得到非籠統的答案LangChain構建代理通過代理去調用Langchain構建RAG的對話應用包含歷史記錄的對話生成Langchia…

Flutter基礎(前端教程①-容器和控件位置)

一個紅色背景的 Container垂直排列的 Column 布局中央的 ElevatedButton按鈕下方的白色文本import package:flutter/material.dart;void main() {runApp(const MyApp()); }class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);overrideWidget bu…

CSS flex

目錄 flex-box和flex-item 主軸和副軸 ?編輯 flex-box的屬性 flex-direction flex-wrap flex-flow justify-content ?編輯?align-items align-content flex-item的屬性 flex-basis flex-grow flex-shrink flex flex-box和flex-item 當把一個塊級元素的displ…

【JMeter】執行系統命令

步驟如下&#xff1a; 添加JSP233 Sampler&#xff1a;右擊線程組>添加>取樣器>JSR223 Sampler2.填寫腳本&#xff0c;執行后查看日志。res "ipconfig".execute().text log.info(res)res "python -c \"print(11)\"".execute().text l…

AI Agent開發學習系列 - langchain之memory(1):內存中的短時記憶

內存中的短時記憶&#xff0c;在 LangChain 中通常指 ConversationBufferMemory 這類“對話緩沖記憶”工具。它的作用是&#xff1a;在內存中保存最近的對話歷史&#xff0c;讓大模型能理解上下文&#xff0c;實現連續對話。 對話緩沖記憶”工具 主要特點 只保留最近的對話內容…

uniapp實現微信小程序端圖片保存到相冊

效果圖展示 安裝插件海報畫板導入到項目里面&#xff0c;在頁面直接使用 <template><view><button click"saveToAlbum" class"save-button">保存到相冊</button><image :src"path" mode"widthFix" v-if&qu…

Java生產帶文字、帶邊框的二維碼

Java 生成帶文字、帶邊框的二維碼1、Java 生成帶文字的二維碼1.1、導入jar包1.2、普通單一的二維碼1.2.1、代碼示例1.2.2、效果1.3、帶文字的二維碼1.&#xff13;.&#xff11;、代碼示例1.3.2、效果2、帶邊框的二維碼2.1、代碼示例2.2、帶邊框的二維碼效果 1、Java 生成帶文字…

ARM單片機啟動流程(三)(棧空間綜合理解及相關實際應用)

文章目錄1、引出棧空間問題2、解決問題2.1、RAM空間2.2、RAM空間具體分布2.3、關于棧空間的使用2.4、棧溢出2.5、變量的消亡2.6、回到關鍵字static2.7、合法性的判斷1、引出棧空間問題 從static關鍵字引出該部分內容。 為什么能從static引出來&#xff1f; 在使用該關鍵字的…

【RK3568+PG2L50H開發板實驗例程】FPGA部分 | 鍵控LED實驗

本原創文章由深圳市小眼睛科技有限公司創作&#xff0c;版權歸本公司所有&#xff0c;如需轉載&#xff0c;需授權并注明出處&#xff08;www.meyesemi.com) 1.實驗簡介 實驗目的&#xff1a; 從創建工程到編寫代碼&#xff0c;完成引腳約束&#xff0c;最后生成 bit 流下載到…

【Python練習】039. 編寫一個函數,反轉一個單鏈表

039. 編寫一個函數,反轉一個單鏈表 039. 編寫一個函數,反轉一個單鏈表方法 1:迭代實現運行結果代碼解釋方法 2:遞歸實現運行結果代碼解釋選擇方法迭代法與遞歸法的區別039. 編寫一個函數,反轉一個單鏈表 在 Python 中,可以通過迭代或遞歸的方式反轉一個單鏈表。 方法 1…

BERT代碼簡單筆記

參考視頻&#xff1a;BERT代碼(源碼)從零解讀【Pytorch-手把手教你從零實現一個BERT源碼模型】_嗶哩嗶哩_bilibili 一、BertTokenizer BertTokenizer 是基于 WordPiece 算法的 BERT 分詞器&#xff0c;繼承自 PreTrainedTokenizer。 繼承的PretrainedTokenizer&#xff0c;具…

PID控制算法理論學習基礎——單級PID控制

這是一篇我在學習PID控制算法的過程中的學習記錄。在一開始學習PID的時候&#xff0c;我也看了市面上許多的資料&#xff0c;好的資料固然有&#xff0c;但是更多的是不知所云。&#xff08;有的是寫的太過深奧&#xff0c;有的則是照搬挪用&#xff0c;對原理則一問三不知&…

【Elasticsearch】function_score與rescore

它們倆都是用來“**干涉評分**”的&#xff0c;但**工作階段不同、性能開銷不同、能做的事也不同**。一句話總結&#xff1a;> **function_score** 在 **第一次算分** 時就動手腳&#xff1b; > **rescore** 在 **拿到 Top-N 結果后** 再“重新打分”。下面把“能干嘛”…

無廣告純凈體驗 WPS2016 精簡版:移除聯網模塊 + 非核心組件,古董電腦也能跑

各位辦公小能手們&#xff01;今天給你們介紹一款超神的辦公軟件——WPS2016精簡版&#xff01;它有多小呢&#xff1f;才33MB&#xff0c;簡直就是軟件界的小不點兒&#xff01;別看它個頭小&#xff0c;功能可一點兒都不含糊&#xff0c;文字、表格、演示這三大功能它全都有。…

《PyWin32:Python與Windows的橋梁,解鎖系統自動化新姿勢》

什么是 PyWin32在 Windows 平臺的 Python 開發領域中&#xff0c;PyWin32 是一個舉足輕重的庫&#xff0c;它為 Python 開發者打開了一扇直接通往 Windows 操作系統底層功能的大門。簡單來說&#xff0c;PyWin32 是用于 Python 訪問 Windows API&#xff08;Application Progra…

vite如何生成gzip,并在服務器上如何設置開啟

1. 安裝插件npm install vite-plugin-compression -D2. 在 vite.config.ts 中配置TypeScriptimport { defineConfig } from vite import compression from vite-plugin-compressionexport default defineConfig({plugins: [compression({algorithm: gzip,ext: .gz,threshold: 1…