FunGame-Godot/scripts/Chapter/ChapterScene.cs
2026-02-03 01:38:51 +08:00

353 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Threading.Tasks;
using Godot;
namespace Milimoe.GodotGame;
public partial class ChapterScene : Node2D, INovelStartEvent, INovelEndEvent, IMenuObject, IFadeInFadeOutBlack
{
[Export]
public PackedScene NovelScene { get; set; }
[Export]
public PackedScene MenuScene { get; set; }
[Export]
public CanvasLayer UI { get; set; }
[Export]
public string ChapterInGame { get; set; }
[Export]
public string AreaInGame { get; set; }
[Export]
public string NovelName { get; set; }
[Export]
public string SceneName { get; set; }
[Export]
public CameraFight CameraFight { get; set; }
[Export]
public CharacterBody Player { get; set; }
[Export]
public PackedScene Fighting { get; set; }
public string NovelNodeKey { get; set; } = "";
private CanvasLayer _novelCanvasLayer;
private CanvasLayer _menuCanvasLayer;
private Node _novelInterface;
private Node _menuInterface;
private AudioStreamPlayer _buttonPlayer;
private AudioStreamPlayer _selectPlayer;
private AudioStreamPlayer _enterFightPlayer;
private Node _fighting;
private UserInterface _userInterface;
public override async void _Ready()
{
_buttonPlayer = GetNode<AudioStreamPlayer>("ButtonPlayer");
_selectPlayer = GetNode<AudioStreamPlayer>("SelectPlayer");
_enterFightPlayer = GetNode<AudioStreamPlayer>("EnterFightPlayer");
if (UI.GetNode<Control>("UserInterface") is UserInterface userInterface)
{
_userInterface = userInterface;
}
GameConstant.InitGame();
Player.BattleStarted += OnBattleStarted;
// 实例化菜单界面场景
if (MenuScene != null)
{
_menuCanvasLayer = GetNode<CanvasLayer>("Menu");
_menuCanvasLayer.Visible = false;
_menuInterface = MenuScene.Instantiate();
if (_menuInterface is MenuController menuController)
{
menuController.Parent = this;
}
_menuCanvasLayer.AddChild(_menuInterface);
}
OnNovelStart(ChapterInGame, AreaInGame, NovelName, SceneName, NovelNodeKey);
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed("toggle_fight_log"))
{
ToggleFightLog();
}
if (@event is InputEventKey keyEvent)
{
if (keyEvent.Pressed && keyEvent.Keycode == Key.Escape)
{
if (_menuCanvasLayer != null)
{
ChangeState();
}
}
}
}
public async void OnNovelStart(string chapter_in_game, string area_in_game, string novel_name, string scene_name, string node_key = "")
{
GameConstant.Pause = true;
GameConstant.ChapterInGame = chapter_in_game;
GameConstant.AreaInGame = area_in_game;
GameConstant.NovelName = novel_name;
GameConstant.SceneName = scene_name;
_novelCanvasLayer = GetNode<CanvasLayer>("Novel");
_novelCanvasLayer.Visible = true;
ColorRect colorRect = GetNode<ColorRect>("ColorRect");
colorRect.SelfModulate = new Color(0, 0, 0, 1);
colorRect.Show();
// 实例化小说界面场景
if (NovelScene != null)
{
_novelInterface = NovelScene.Instantiate();
_novelCanvasLayer.AddChild(_novelInterface);
if (_novelInterface is NovelController novelController)
{
novelController.ChapterObject = this;
novelController.MenuObject = this;
novelController.FadeObject = this;
await FadeOutBlack(1f, true);
novelController.InitNovel(novel_name, scene_name, node_key);
GameConstant.AutoSave(0);
}
}
}
public async void OnNovelEnd(CanvasLayer node, string novel_name, string scene_name)
{
node.Visible = false;
await FadeInBlack(1f);
await Task.Delay(500);
await FadeOutBlack(0.5f, true);
if (UI != null)
{
GameConstant.Pause = false;
UI.Visible = true;
if (UI.GetNode<Control>("UserInterface") is UserInterface userInterface)
{
if (userInterface.Location != null)
{
userInterface.Location.Text = GameConstant.AreaInGame;
}
if (userInterface.Rayne is CharacterStatus cs1)
{
cs1.UpdateLabel();
}
if (userInterface.Irene is CharacterStatus cs2)
{
cs2.UpdateLabel();
}
userInterface.FightLog.Visible = false;
userInterface.RoundQueue.Visible = false;
//if (GameConstant.Characters.Count == 1)
//{
// userInterface.CharacterStatus1.Visible = true;
// TextureRect image = userInterface.CharacterStatus1.GetNode<TextureRect>("Image");
// using Texture2D texture = GD.Load<CompressedTexture2D>("res://assets/character/雷恩.png");
// image.Texture = texture;
//}
//else if (GameConstant.Characters.Count == 2)
//{
// userInterface.CharacterStatus1.Visible = true;
// userInterface.CharacterStatus2.Visible = true;
//}
}
}
}
public async Task FadeInBlack(float fadeTime, bool hide = false)
{
// 淡入黑色遮罩
ColorRect colorRect = GetNode<ColorRect>("ColorRect");
colorRect.SelfModulate = new Color(0, 0, 0, 0);
colorRect.Show();
Tween tween = CreateTween();
tween.TweenProperty(colorRect, "self_modulate", new Color(0, 0, 0, 1), fadeTime);
await ToSignal(tween, "finished");
if (hide) colorRect.Hide();
}
public async Task FadeOutBlack(float fadeTime, bool hide = false)
{
// 淡出黑色遮罩
ColorRect colorRect = GetNode<ColorRect>("ColorRect");
colorRect.SelfModulate = new Color(0, 0, 0, 1);
colorRect.Show();
Tween tween = CreateTween();
tween.TweenProperty(colorRect, "self_modulate", new Color(0, 0, 0, 0), fadeTime);
await ToSignal(tween, "finished");
if (hide) colorRect.Hide();
}
public void ChangeState()
{
GD.Print("Change Menu State!");
if (_menuCanvasLayer != null)
{
_menuCanvasLayer.Visible = !_menuCanvasLayer.Visible;
}
}
private async void OnBattleStarted(CharacterBody2D player, CharacterBody2D enemy)
{
EnemyBody enemyBody = null;
if (enemy is EnemyBody body)
{
enemyBody = body;
enemyBody.Enabled = false;
}
GD.Print("信号已接收:开始切换至战斗摄像机");
GameConstant.Pause = true;
Camera2D Camera = GetNode<Camera2D>("Player/Camera2D");
Vector2 targetZoom = Camera.Zoom;
CanvasLayer ColorRectCanvasLayer = GetNode<CanvasLayer>("ColorRectCanvasLayer");
ColorRectCanvasLayer.Visible = true;
ColorRect BlurOverlay = ColorRectCanvasLayer.GetNode<ColorRect>("BlurOverlay");
_enterFightPlayer.Play();
GetNode<AnimationPlayer>("战斗提示动画").Play("fight_start");
PlayZoomBlurTransition(Camera, BlurOverlay);
Engine.TimeScale = 0.05f;
ShakeCamera(Camera, 0.2f, 35.0f);
await Task.Delay(1000);
Engine.TimeScale = 1.0f;
AudioStreamPlayer MapMusic = GetNode<AudioStreamPlayer>("MapMusic");
if (MapMusic.Playing)
{
MapMusic.StreamPaused = true;
}
AudioStreamPlayer FightMusic = GetNode<AudioStreamPlayer>("FightMusic");
FightMusic.Play();
FightMusic.StreamPaused = false;
Vector2I battleStartCell = new(100, 10);
// 如果 player 脚本里实现了上面的 TeleportToTile
if (player is CharacterBody myPlayer)
{
myPlayer.TeleportToTile(GetNode<TileMapLayer>("TileMapLayer"), battleStartCell);
}
// 切换当前摄像机
if (CameraFight != null)
{
CameraFight.Enabled = true;
CameraFight.UpdateLimitsFromTileMap();
CameraFight.MakeCurrent();
// 激活摄像机的移动逻辑
CameraFight.SetPhysicsProcess(true);
}
_fighting = Fighting.Instantiate();
Panel fightLog = null;
UserInterface userInterface = null;
if (UI.GetNode<Control>("UserInterface") is UserInterface tempUI)
{
userInterface = tempUI;
userInterface.Location.Text = "";
fightLog = userInterface.FightLog;
fightLog.Visible = true;
userInterface.RoundQueue.Visible = true;
}
GetTree().CreateTween().TweenProperty(Camera, "zoom", targetZoom, 0.2f);
ColorRectCanvasLayer.Visible = false;
if (_fighting is Fighting fighting)
{
fighting.ParentUI = this;
fighting.TileMapLayer = GetNode<TileMapLayer>("TileMapLayer");
fighting.CameraFight = CameraFight;
fighting.Character = GameConstant.Characters[1];
fighting.FightLog = fightLog?.GetNode<RichTextLabel>("RichTextLabel");
fighting.StartCell = battleStartCell;
fighting.Enemy = enemyBody;
await fighting.StartGame(false, true);
// 战斗结束恢复场景
if (enemyBody != null && enemyBody.Dead && IsInstanceValid(enemy))
{
enemy.QueueFree();
}
CameraFight?.ExitBattleMode();
if (player is CharacterBody myPlayer2)
{
myPlayer2.ReturnToSavedPosition();
}
if (userInterface != null)
{
userInterface.Location.Text = GameConstant.AreaInGame;
userInterface.FightLog.Visible = false;
userInterface.RoundQueue.Visible = false;
}
}
}
private void ShakeCamera(Camera2D camera, float duration, float intensity)
{
Tween tween = GetTree().CreateTween();
for (int i = 0; i < 5; i++)
{
Vector2 randomOffset = new(
(float)GD.RandRange(-intensity, intensity),
(float)GD.RandRange(-intensity, intensity)
);
// 快速来回摆动
tween.TweenProperty(camera, "offset", randomOffset, duration / 5);
}
// 最后归位
tween.TweenProperty(camera, "offset", Vector2.Zero, 0.5f);
}
private void PlayZoomBlurTransition(Camera2D camera, ColorRect BlurOverlay)
{
// 确保模糊层可见
BlurOverlay.Visible = true;
ShaderMaterial mat = BlurOverlay.Material as ShaderMaterial;
// 创建补间动画
Tween tween = GetTree().CreateTween();
tween.SetIgnoreTimeScale(true);
// 设置并行播放,让拉近和模糊同时发生
tween.SetParallel(true);
// 1. 镜头拉近动画 (ExpoOut 效果最像“冲刺”)
Vector2 targetZoom = camera.Zoom * 1.5f;
tween.TweenProperty(camera, "zoom", targetZoom, 0.3f)
.SetTrans(Tween.TransitionType.Expo)
.SetEase(Tween.EaseType.Out);
// 2. Shader 模糊强度动画
tween.TweenMethod(Callable.From<float>((val) => {
mat.SetShaderParameter("blur_amount", val);
}), 0.0f, 5f, 0.2f);
// 3. (可选) 稍微等待一下后,执行下一步逻辑
tween.Chain().SetParallel(false);
tween.TweenCallback(Callable.From(() => {
GD.Print("拉近完成切换到战斗UI或开始战斗");
}));
}
private void ToggleFightLog()
{
if (_userInterface != null && _userInterface.FightLog != null)
{
// 这一行代码就能实现“可见”与“不可见”的快速取反
_userInterface.FightLog.Visible = !_userInterface.FightLog.Visible;
GD.Print($"战斗日志状态: {(_userInterface.FightLog.Visible ? "" : "")}");
}
}
}