cocos-dating-game/assets/scripts/StoryManager.ts
openclaw_frontend_developer 41e7ca05f1 Initial commit: Cocos Creator恋爱游戏引擎
- StoryManager: 剧情管理器
- DialogueBox: 对话框组件(带打字机效果)
- CharacterView: 立绘组件
- ChoiceButton: 选项按钮
- AffectionSystem: 好感度系统
- chapter1.json: 示例剧情
2026-03-11 04:54:43 +00:00

281 lines
7.1 KiB
TypeScript

import { _decorator, Component, Node, resources, JsonAsset, Sprite, SpriteFrame, Label, Button, director } from 'cc';
import { DialogueBox } from './DialogueBox';
import { CharacterView } from './CharacterView';
import { ChoiceButton } from './ChoiceButton';
import { AffectionSystem } from './AffectionSystem';
const { ccclass, property } = _decorator;
// 剧情数据结构
interface StoryData {
title: string;
scenes: SceneData[];
}
interface SceneData {
id: string;
background?: string;
characters: CharacterShowData[];
dialogue: DialogueData[];
choices?: ChoiceData[];
nextScene?: string;
}
interface CharacterShowData {
id: string;
name: string;
emotion: string;
position: 'left' | 'center' | 'right';
visible: boolean;
}
interface DialogueData {
speaker?: string;
text: string;
emotion?: string;
}
interface ChoiceData {
text: string;
nextScene: string;
affectionChange?: { [key: string]: number };
}
@ccclass('StoryManager')
export class StoryManager extends Component {
private static _instance: StoryManager = null;
public static get instance(): StoryManager {
return StoryManager._instance;
}
@property(DialogueBox)
dialogueBox: DialogueBox;
@property(CharacterView)
characterView: CharacterView;
@property(Node)
choiceContainer: Node;
@property(AffectionSystem)
affectionSystem: AffectionSystem;
private storyData: StoryData = null;
private currentScene: SceneData = null;
private currentDialogueIndex: number = 0;
private isTyping: boolean = false;
onLoad() {
StoryManager._instance = this;
}
start() {
// 自动加载第一章
this.loadChapter('chapter1');
}
/**
* 加载剧情章节
*/
loadChapter(chapterId: string) {
resources.load(`story/${chapterId}`, JsonAsset, (err, jsonAsset) => {
if (err) {
console.error('加载剧情失败:', err);
return;
}
this.storyData = jsonAsset.json as StoryData;
console.log('剧情加载成功:', this.storyData.title);
// 开始播放第一个场景
if (this.storyData.scenes.length > 0) {
this.playScene(this.storyData.scenes[0].id);
}
});
}
/**
* 播放指定场景
*/
playScene(sceneId: string) {
const scene = this.storyData.scenes.find((s) => s.id === sceneId);
if (!scene) {
console.error('未找到场景:', sceneId);
return;
}
this.currentScene = scene;
this.currentDialogueIndex = 0;
// 设置背景
if (scene.background) {
this.loadBackground(scene.background);
}
// 更新角色显示
this.updateCharacters(scene.characters);
// 隐藏选项
this.hideChoices();
// 开始对话
this.showNextDialogue();
}
/**
* 显示下一句对话
*/
showNextDialogue() {
if (!this.currentScene) return;
// 检查是否还有对话
if (this.currentDialogueIndex >= this.currentScene.dialogue.length) {
this.onDialogueEnd();
return;
}
const dialogue = this.currentScene.dialogue[this.currentDialogueIndex];
// 更新对话框
this.dialogueBox.show(dialogue.speaker, dialogue.text, () => {
this.isTyping = false;
});
// 更新立绘表情
if (dialogue.emotion && this.characterView) {
this.characterView.setEmotion(dialogue.emotion);
}
this.currentDialogueIndex++;
this.isTyping = true;
}
/**
* 点击对话区域继续
*/
onDialogueClicked() {
if (this.isTyping) {
// 打字时点击直接显示完整文本
this.dialogueBox.finishTyping();
this.isTyping = false;
} else {
// 显示下一句
this.showNextDialogue();
}
}
/**
* 对话结束处理
*/
private onDialogueEnd() {
if (!this.currentScene) return;
// 检查是否有选项
if (this.currentScene.choices && this.currentScene.choices.length > 0) {
this.showChoices(this.currentScene.choices);
}
// 检查是否有下一场景
else if (this.currentScene.nextScene) {
this.playScene(this.currentScene.nextScene);
}
else {
console.log('剧情结束');
}
}
/**
* 显示选项
*/
private showChoices(choices: ChoiceData[]) {
this.hideChoices();
choices.forEach((choice, index) => {
const choiceNode = this.choiceContainer.children[index];
if (choiceNode) {
choiceNode.active = true;
const choiceBtn = choiceNode.getComponent(ChoiceButton);
if (choiceBtn) {
choiceBtn.setup(choice.text, () => {
this.onChoiceSelected(choice);
});
}
}
});
}
/**
* 隐藏选项
*/
private hideChoices() {
this.choiceContainer.children.forEach(child => {
child.active = false;
});
}
/**
* 选择选项
*/
private onChoiceSelected(choice: ChoiceData) {
// 更新好感度
if (choice.affectionChange) {
this.affectionSystem.changeAffection(choice.affectionChange);
}
// 跳转到下一场景
if (choice.nextScene) {
this.playScene(choice.nextScene);
}
}
/**
* 加载背景图
*/
private loadBackground(bgPath: string) {
resources.load(`backgrounds/${bgPath}`, SpriteFrame, (err, spriteFrame) => {
if (err) {
console.error('加载背景失败:', err);
return;
}
// 假设有一个背景节点
const bgNode = this.node.getChildByName('Background');
if (bgNode) {
const sprite = bgNode.getComponent(Sprite);
if (sprite) {
sprite.spriteFrame = spriteFrame;
}
}
});
}
/**
* 更新角色显示
*/
private updateCharacters(characters: CharacterShowData[]) {
if (!this.characterView) return;
characters.forEach(char => {
if (char.visible) {
this.characterView.showCharacter(char.id, char.name, char.emotion, char.position);
} else {
this.characterView.hideCharacter(char.id);
}
});
}
/**
* 跳转到指定场景(可外部调用)
*/
public goToScene(sceneId: string) {
this.playScene(sceneId);
}
/**
* 重新开始
*/
public restart() {
if (this.storyData && this.storyData.scenes.length > 0) {
this.affectionSystem.reset();
this.playScene(this.storyData.scenes[0].id);
}
}
}