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

184 lines
4.9 KiB
TypeScript

import { _decorator, Component, Node, Sprite, SpriteFrame, resources, tween, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
// 角色位置
type CharacterPosition = 'left' | 'center' | 'right';
// 角色数据
interface CharacterData {
id: string;
name: string;
emotion: string;
position: CharacterPosition;
visible: boolean;
}
@ccclass('CharacterView')
export class CharacterView extends Component {
@property(Node)
leftCharacter: Node;
@property(Node)
centerCharacter: Node;
@property(Node)
rightCharacter: Node;
// 角色资源缓存
private characterAssets: Map<string, Map<string, SpriteFrame>> = new Map();
onLoad() {
// 初始化所有角色隐藏
this.hideAllCharacters();
}
/**
* 显示角色
*/
showCharacter(characterId: string, name: string, emotion: string, position: CharacterPosition) {
const node = this.getCharacterNode(position);
if (!node) return;
// 加载角色立绘
this.loadCharacterSprite(characterId, emotion, (spriteFrame) => {
if (spriteFrame) {
const sprite = node.getComponent(Sprite);
if (sprite) {
sprite.spriteFrame = spriteFrame;
}
// 显示节点
node.active = true;
// 播放出场动画
this.playEnterAnimation(node);
}
});
}
/**
* 隐藏角色
*/
hideCharacter(characterId: string) {
// 遍历所有位置查找并隐藏
[this.leftCharacter, this.centerCharacter, this.rightCharacter].forEach(node => {
if (node && node.active) {
node.active = false;
}
});
}
/**
* 隐藏所有角色
*/
hideAllCharacters() {
[this.leftCharacter, this.centerCharacter, this.rightCharacter].forEach(node => {
if (node) {
node.active = false;
}
});
}
/**
* 切换角色表情
*/
setEmotion(emotion: string) {
// 这个方法需要在具体场景中根据当前显示的角色来实现
// 可以通过在场景中保存当前角色ID来调用
console.log('切换表情:', emotion);
}
/**
* 获取角色节点
*/
private getCharacterNode(position: CharacterPosition): Node {
switch (position) {
case 'left':
return this.leftCharacter;
case 'center':
return this.centerCharacter;
case 'right':
return this.rightCharacter;
default:
return this.centerCharacter;
}
}
/**
* 加载角色立绘
*/
private loadCharacterSprite(characterId: string, emotion: string, callback: (spriteFrame: SpriteFrame) => void) {
const path = `characters/${characterId}/${emotion}`;
// 检查缓存
if (this.characterAssets.has(characterId)) {
const emotionMap = this.characterAssets.get(characterId);
if (emotionMap && emotionMap.has(emotion)) {
callback(emotionMap.get(emotion));
return;
}
}
// 加载新资源
resources.load(path, SpriteFrame, (err, spriteFrame) => {
if (err) {
console.warn(`加载角色立绘失败: ${path}`, err);
callback(null);
return;
}
// 缓存
if (!this.characterAssets.has(characterId)) {
this.characterAssets.set(characterId, new Map());
}
this.characterAssets.get(characterId).set(emotion, spriteFrame);
callback(spriteFrame);
});
}
/**
* 播放出场动画
*/
private playEnterAnimation(node: Node) {
// 从下方进入
const originalPos = node.position.clone();
node.setPosition(originalPos.x, originalPos.y - 200, originalPos.z);
tween(node)
.to(0.3, { position: originalPos }, { easing: 'backOut' })
.start();
}
/**
* 播放退场动画
*/
playExitAnimation(node: Node, callback?: () => void) {
tween(node)
.to(0.3, { position: new Vec3(node.position.x, node.position.y - 200, node.position.z) })
.call(() => {
node.active = false;
if (callback) callback();
})
.start();
}
/**
* 预加载角色资源
*/
preloadCharacter(characterId: string, emotions: string[]) {
emotions.forEach(emotion => {
const path = `characters/${characterId}/${emotion}`;
resources.preload(path, SpriteFrame);
});
}
/**
* 清除缓存
*/
clearCache() {
this.characterAssets.clear();
}
}