头像系统实现
在这个文件夹创建:include\dialogue_portraits.h
#ifndef GUARD_DIALOGUE_PORTRAITS_H
#define GUARD_DIALOGUE_PORTRAITS_H
#include "global.h"
// 头像位置定义
#define PORTRAIT_LEFT_X 2 // 左侧头像位置
#define PORTRAIT_RIGHT_X 21 // 右侧头像位置
#define PORTRAIT_Y 7 // 对话框上方位置
// 头像状态枚举
enum PortraitState {
PORTRAIT_STATE_NONE = 0,
PORTRAIT_STATE_SHOWING,
PORTRAIT_STATE_VISIBLE,
PORTRAIT_STATE_HIDING
};
// 头像结构体
struct DialoguePortrait {
u8 spriteId; // 精灵ID
u8 state; // 当前状态
u8 currentFrame; // 当前帧
u8 timer; // 动画计时器
u16 paletteTag; // 调色板标签
u16 gfxTag; // 图形标签
};
// 函数声明
void InitDialoguePortraitSystem(void);
void CleanupDialoguePortraitSystem(void);
bool8 ShowDialoguePortrait(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag);
bool8 ShowDialoguePortraitWithFlip(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag, bool8 hFlip, bool8 vFlip);
void HideDialoguePortrait(u8 side);
void UpdateDialoguePortraits(void);
bool8 ArePortraitsActive(void);
void SetDialoguePortraitFlip(u8 side, bool8 hFlip, bool8 vFlip);
#endif // GUARD_DIALOGUE_PORTRAITS_H
在这个文件夹创建:src\dialogue_portraits.c
#include "global.h"
#include "dialogue_portraits.h"
#include "window.h"
#include "menu.h"
#include "sprite.h"
#include "malloc.h"
#include "task.h"
#include "text_window.h"
#include "constants/trainers.h"
#include "data.h"
#include "decompress.h"
#include "constants/songs.h"
#include "portrait_data.h"
// 自定义头像ID范围定义
#define CUSTOM_PORTRAIT_ID_START 1000
#define CUSTOM_PORTRAIT_ID_END 1999
// 头像系统全局变量
static EWRAM_DATA struct DialoguePortrait sPortraits[2] = {0};
static EWRAM_DATA bool8 sPortraitSystemInitialized = FALSE;
// 自定义头像资源声明
extern const u8 gHeadPortrait_001_Gfx[];
extern const u16 gHeadPortrait_001_Pal[];
extern const u8 gHeadPortrait_002_Gfx[];
extern const u16 gHeadPortrait_002_Pal[];
// 加载自定义头像资源
static bool8 LoadCustomPortrait(u16 portraitId, u16 *gfxTag, u16 *paletteTag)
{
u16 localId = portraitId - CUSTOM_PORTRAIT_ID_START + 1;
// 根据ID加载对应的自定义头像资源
const u8 *gfxData = NULL;
const u16 *palData = NULL;
switch (localId) {
case 1:
gfxData = gHeadPortrait_001_Gfx;
palData = gHeadPortrait_001_Pal;
break;
case 2:
gfxData = gHeadPortrait_002_Gfx;
palData = gHeadPortrait_002_Pal;
break;
default:
return FALSE; // 不支持的自定义头像ID
}
if (!gfxData || !palData)
return FALSE;
// 加载自定义头像图形
struct CompressedSpriteSheet spriteSheet = {
.data = (const void *)gfxData,
.size = 0x800, // 64x64像素,4bpp
.tag = portraitId + 0x3000 // 使用0x3000系列标签避免与训练师冲突
};
LoadCompressedSpriteSheet(&spriteSheet);
// 加载自定义头像调色板
struct SpritePalette spritePalette = {
.data = (const void *)palData,
.tag = portraitId + 0x4000 // 使用0x4000系列标签
};
LoadSpritePalette(&spritePalette);
*gfxTag = portraitId + 0x3000;
*paletteTag = portraitId + 0x4000;
return TRUE;
}
// 初始化头像系统
void InitDialoguePortraitSystem(void)
{
// 如果已经初始化,先清理
if (sPortraitSystemInitialized) {
CleanupDialoguePortraitSystem();
}
// 初始化头像数据
memset(sPortraits, 0, sizeof(sPortraits));
// 初始化资源标签为无效值
for (int i = 0; i < 2; i++) {
sPortraits[i].spriteId = 0xFF;
sPortraits[i].gfxTag = 0xFFFF;
sPortraits[i].paletteTag = 0xFFFF;
}
sPortraitSystemInitialized = TRUE;
}
// 清理头像系统
void CleanupDialoguePortraitSystem(void)
{
if (!sPortraitSystemInitialized)
return;
// 销毁精灵
if (sPortraits[0].spriteId != 0xFF && sPortraits[0].spriteId < MAX_SPRITES) {
DestroySprite(&gSprites[sPortraits[0].spriteId]);
}
if (sPortraits[1].spriteId != 0xFF && sPortraits[1].spriteId < MAX_SPRITES) {
DestroySprite(&gSprites[sPortraits[1].spriteId]);
}
memset(sPortraits, 0, sizeof(sPortraits));
sPortraitSystemInitialized = FALSE;
}
// 显示头像
bool8 ShowDialoguePortrait(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag)
{
if (!sPortraitSystemInitialized || side > 1)
return FALSE;
struct DialoguePortrait *portrait = &sPortraits[side];
// 如果已经有头像显示,先隐藏并清理
if (portrait->state != PORTRAIT_STATE_NONE) {
HideDialoguePortrait(side);
}
// 加载头像图形资源
if (gfxTag >= CUSTOM_PORTRAIT_ID_START && gfxTag <= CUSTOM_PORTRAIT_ID_END) {
// 使用自定义头像
if (!LoadCustomPortrait(gfxTag, &portrait->gfxTag, &portrait->paletteTag)) {
return FALSE;
}
} else if (gfxTag < TRAINER_PIC_COUNT) {
// 使用系统预定义的训练师图像
const struct TrainerSprite *trainerSprite = &gTrainerSprites[gfxTag];
// 加载图像数据
struct CompressedSpriteSheet spriteSheet = {
.data = trainerSprite->frontPic.data,
.size = trainerSprite->frontPic.size,
.tag = gfxTag + 0x1000 // 使用唯一标签避免冲突
};
LoadCompressedSpriteSheet(&spriteSheet);
// 加载调色板数据
struct SpritePalette spritePalette = {
.data = trainerSprite->palette.data,
.tag = gfxTag + 0x2000 // 使用唯一标签避免冲突
};
LoadSpritePalette(&spritePalette);
// 更新标签以便后续使用
portrait->gfxTag = gfxTag + 0x1000;
portrait->paletteTag = gfxTag + 0x2000;
} else {
// 对于无效的ID,直接返回失败
return FALSE;
}
// 创建精灵来显示头像
struct SpriteTemplate spriteTemplate = {
.tileTag = portrait->gfxTag,
.paletteTag = portrait->paletteTag,
.oam = &(const struct OamData){
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
.x = 0,
.size = SPRITE_SIZE(64x64),
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
},
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
// 计算精灵位置
u16 x, y;
if (side == 0) { // 左侧
x = PORTRAIT_LEFT_X * 8 + 32; // 窗口中心位置
} else { // 右侧
x = PORTRAIT_RIGHT_X * 8 + 32;
}
y = PORTRAIT_Y * 8 + 32;
portrait->spriteId = CreateSprite(&spriteTemplate, x, y, 0);
if (portrait->spriteId == MAX_SPRITES) {
// 创建精灵失败,清理资源
FreeSpritePaletteByTag(portrait->paletteTag);
FreeSpriteTilesByTag(portrait->gfxTag);
portrait->gfxTag = 0xFFFF;
portrait->paletteTag = 0xFFFF;
return FALSE;
}
// 设置精灵属性
struct Sprite *sprite = &gSprites[portrait->spriteId];
sprite->oam.priority = 1; // 在对话框之上
sprite->subpriority = 4;
// 设置状态
portrait->state = PORTRAIT_STATE_SHOWING;
portrait->currentFrame = 0;
portrait->timer = 0;
return TRUE;
}
// 显示头像(带翻转参数)
bool8 ShowDialoguePortraitWithFlip(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag, bool8 hFlip, bool8 vFlip)
{
if (!ShowDialoguePortrait(portraitId, side, gfxTag, paletteTag))
return FALSE;
// 设置翻转状态
SetDialoguePortraitFlip(side, hFlip, vFlip);
return TRUE;
}
// 隐藏头像
void HideDialoguePortrait(u8 side)
{
if (!sPortraitSystemInitialized || side > 1)
return;
struct DialoguePortrait *portrait = &sPortraits[side];
if (portrait->state == PORTRAIT_STATE_NONE)
return;
// 销毁精灵
if (portrait->spriteId != 0xFF && portrait->spriteId < MAX_SPRITES) {
DestroySprite(&gSprites[portrait->spriteId]);
portrait->spriteId = 0xFF;
}
// 释放资源
if (portrait->gfxTag != 0xFFFF) {
FreeSpritePaletteByTag(portrait->paletteTag);
FreeSpriteTilesByTag(portrait->gfxTag);
}
// 重置所有状态和标签
portrait->state = PORTRAIT_STATE_NONE;
portrait->currentFrame = 0;
portrait->timer = 0;
portrait->gfxTag = 0xFFFF;
portrait->paletteTag = 0xFFFF;
}
// 更新头像
void UpdateDialoguePortraits(void)
{
if (!sPortraitSystemInitialized)
return;
for (int i = 0; i < 2; i++) {
struct DialoguePortrait *portrait = &sPortraits[i];
switch (portrait->state) {
case PORTRAIT_STATE_SHOWING:
portrait->timer++;
if (portrait->timer > 10) { // 显示动画持续10帧
portrait->state = PORTRAIT_STATE_VISIBLE;
}
break;
case PORTRAIT_STATE_VISIBLE:
// 可以在这里添加呼吸动画等效果
portrait->timer++;
if (portrait->timer > 60) { // 每秒更新一次
portrait->timer = 0;
portrait->currentFrame = (portrait->currentFrame + 1) % 2;
}
break;
case PORTRAIT_STATE_HIDING:
portrait->timer++;
if (portrait->timer > 10) { // 隐藏动画持续10帧
portrait->state = PORTRAIT_STATE_NONE;
}
break;
default:
break;
}
}
}
// 设置头像翻转
void SetDialoguePortraitFlip(u8 side, bool8 hFlip, bool8 vFlip)
{
if (!sPortraitSystemInitialized || side > 1)
return;
struct DialoguePortrait *portrait = &sPortraits[side];
// 检查是否有有效的精灵
if (portrait->spriteId == 0xFF || portrait->spriteId >= MAX_SPRITES)
return;
// 检查精灵是否处于活动状态
if (portrait->state == PORTRAIT_STATE_NONE)
return;
struct Sprite *sprite = &gSprites[portrait->spriteId];
// 设置精灵的翻转属性
sprite->hFlip = hFlip;
sprite->vFlip = vFlip;
// 直接设置OAM翻转位,不使用异或操作
sprite->oam.matrixNum &= 0x7; // 清除翻转位
if (hFlip)
sprite->oam.matrixNum |= (1 << 3); // 设置水平翻转位
if (vFlip)
sprite->oam.matrixNum |= (1 << 4); // 设置垂直翻转位
}
// 检查是否有头像活动
bool8 ArePortraitsActive(void)
{
if (!sPortraitSystemInitialized)
return FALSE;
return (sPortraits[0].state != PORTRAIT_STATE_NONE ||
sPortraits[1].state != PORTRAIT_STATE_NONE);
}
在这个文件夹创建:src\portrait_data.c
#include "global.h"
// 自定义头像资源定义
// 这些将由工具链从graphics/head/文件夹中的PNG和PAL文件自动生成
// 头像001 - 玩家头像
const u8 gHeadPortrait_001_Gfx[] = INCBIN_U8("graphics/head/head_001.4bpp.smol");
const u16 gHeadPortrait_001_Pal[] = INCBIN_U16("graphics/head/head_001.gbapal");
// 头像002 - 预留给其他角色
const u8 gHeadPortrait_002_Gfx[] = INCBIN_U8("graphics/head/head_002.4bpp.smol");
const u16 gHeadPortrait_002_Pal[] = INCBIN_U16("graphics/head/head_002.gbapal");
修改这个文件:src\field_message_box.c
#include "global.h"
#include "menu.h"
#include "string_util.h"
#include "task.h"
#include "text.h"
#include "match_call.h"
#include "field_message_box.h"
#include "text_window.h"
#include "script.h"
#include "dialogue_portraits.h" //新增代码
void InitFieldMessageBox(void)
{
sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
gTextFlags.canABSpeedUpPrint = FALSE;
gTextFlags.useAlternateDownArrow = FALSE;
gTextFlags.autoScroll = FALSE;
gTextFlags.forceMidTextSpeed = FALSE;
InitDialoguePortraitSystem(); //新增代码
}
#define tState data[0]
static void Task_DrawFieldMessage(u8 taskId)
{
struct Task *task = &gTasks[taskId];
switch (task->tState)
{
case 0:
if (gMsgIsSignPost)
LoadSignPostWindowFrameGfx();
else
LoadMessageBoxAndBorderGfx();
task->tState++;
break;
case 1:
DrawDialogueFrame(0, TRUE);
task->tState++;
break;
case 2:
UpdateDialoguePortraits(); //新增代码
if (RunTextPrintersAndIsPrinter0Active() != TRUE)
{
sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
DestroyTask(taskId);
}
}
}
void HideFieldMessageBox(void)
{
DestroyTask_DrawFieldMessage();
ClearDialogWindowAndFrame(0, TRUE);
CleanupDialoguePortraitSystem(); //新增代码
sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
}
这个文件添加代码:data\specials.inc
#include "dialogue_portraits.h" //新增代码
// 头像系统special函数实现
void Special_InitDialoguePortraitSystem(void)
{
InitDialoguePortraitSystem();
}
void Special_ShowDialoguePortrait(void)
{
u8 side = gSpecialVar_0x8004; // 0=左侧,1=右侧
u8 portraitId = gSpecialVar_0x8005; // 头像ID
u16 gfxTag = gSpecialVar_0x8006; // 图形标签
u16 paletteTag = gSpecialVar_0x8007; // 调色板标签
ShowDialoguePortrait(portraitId, side, gfxTag, paletteTag);
}
void Special_ShowDialoguePortraitWithFlip(void)
{
u8 side = gSpecialVar_0x8004; // 0=左侧,1=右侧
u8 portraitId = gSpecialVar_0x8005; // 头像ID
u16 gfxTag = gSpecialVar_0x8006; // 图形标签
u16 paletteTag = gSpecialVar_0x8007; // 调色板标签
bool8 hFlip = gSpecialVar_0x8008; // 水平翻转
bool8 vFlip = gSpecialVar_0x8009; // 垂直翻转
ShowDialoguePortraitWithFlip(portraitId, side, gfxTag, paletteTag, hFlip, vFlip);
}
void Special_HideDialoguePortrait(void)
{
u8 side = gSpecialVar_0x8004; // 0=左侧,1=右侧
HideDialoguePortrait(side);
}
void Special_UpdateDialoguePortraits(void)
{
UpdateDialoguePortraits();
}
void Special_CleanupDialoguePortraitSystem(void)
{
CleanupDialoguePortraitSystem();
}
void Special_SetDialoguePortraitFlip(void)
{
u8 side = gSpecialVar_0x8004; // 0=左侧,1=右侧
bool8 hFlip = gSpecialVar_0x8005; // 水平翻转
bool8 vFlip = gSpecialVar_0x8006; // 垂直翻转
SetDialoguePortraitFlip(side, hFlip, vFlip);
}
新增文件:include\portrait_data.h
#ifndef GUARD_PORTRAIT_DATA_H
#define GUARD_PORTRAIT_DATA_H
// 自定义头像资源声明
// 这些将由工具链从graphics/head/文件夹中的PNG和PAL文件生成
extern const u8 gHeadPortrait_001_Gfx[];
extern const u16 gHeadPortrait_001_Pal[];
extern const u8 gHeadPortrait_002_Gfx[];
extern const u16 gHeadPortrait_002_Pal[];
#endif // GUARD_PORTRAIT_DATA_H
文件新增代码:include\constants\field_specials.h
// 头像系统Special函数常量
#define SPECIAL_INIT_DIALOGUE_PORTRAIT_SYSTEM 0x200
#define SPECIAL_SHOW_DIALOGUE_PORTRAIT 0x201
#define SPECIAL_HIDE_DIALOGUE_PORTRAIT 0x202
#define SPECIAL_UPDATE_DIALOGUE_PORTRAITS 0x203
#define SPECIAL_CLEANUP_DIALOGUE_PORTRAIT_SYSTEM 0x204
文件新增代码:graphics_file_rules.mk
### Custom Trainer Portraits ###
# Define custom graphics directory
CUSTOMGFXDIR := graphics/head
# Convert PNG to 4bpp format for trainer portraits
$(CUSTOMGFXDIR)/head_001.4bpp: %.4bpp: %.png
$(GFX) $< $@ -num_tiles 64 -Wnum_tiles
# Convert 4bpp to compressed .4bpp.smol format
$(CUSTOMGFXDIR)/head_001.4bpp.smol: %.4bpp.smol: %.4bpp
$(SMOL) -w $< $@
# Extract palette from PNG and convert to GBAPAL format
$(CUSTOMGFXDIR)/head_001.gbapal: %.gbapal: %.png
$(GFX) $< $@ -palette
脚本测试示例:
Text_PortraitFlipIntro:
.string "这是头像翻转功能演示。\n我们将展示各种翻转效果。$"
Text_PortraitNormal:
.string "这是正常方向的头像。$"
Text_PortraitHFlip:
.string "这是水平翻转的头像。\n头像现在面向左边。$"
Text_PortraitVFlip:
.string "这是垂直翻转的头像。\n头像现在上下颠倒。$"
Text_PortraitBothFlip:
.string "这是同时水平和垂直翻转的头像。\n相当于180度旋转。$"
Text_PortraitRestored:
.string "恢复正常方向的头像。$"
Text_DualPortraitIntro:
.string "现在演示双人对话中的翻转效果。$"
Text_LeftCharacterNormal:
.string "左边角色:你好!我是面向右边的。$"
Text_RightCharacterFlipped:
.string "右边角色:你好!我现在面向左边。$"
Text_BothFacingForward:
.string "两个角色都面向前方了!$"
Text_DynamicFlipIntro:
.string "演示动态翻转效果,模拟角色转头。$"
Text_FacingRight:
.string "面向右边...$"
Text_FacingLeft:
.string "面向左边...$"
Text_FacingRightAgain:
.string "又面向右边了!$"
Text_DemoComplete:
.string "头像翻转功能演示完成!$"
// 示例1:基本头像显示和翻转
EventScript_PortraitFlipBasic::
lock
faceplayer
// 初始化头像系统
special Special_InitDialoguePortraitSystem
msgbox Text_PortraitFlipIntro, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 显示左侧头像(使用自定义头像ID)
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // 头像ID 0 (未使用)
setvar VAR_0x8006, 1000 // 自定义头像ID: head_001.png
setvar VAR_0x8007, 0 // 调色板标签 (未使用)
special Special_ShowDialoguePortrait
msgbox Text_PortraitNormal, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 水平翻转头像
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 1 // hFlip=TRUE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_PortraitHFlip, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 垂直翻转头像
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // hFlip=FALSE
setvar VAR_0x8006, 1 // vFlip=TRUE
special Special_SetDialoguePortraitFlip
msgbox Text_PortraitVFlip, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 同时水平和垂直翻转
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 1 // hFlip=TRUE
setvar VAR_0x8006, 1 // vFlip=TRUE
special Special_SetDialoguePortraitFlip
msgbox Text_PortraitBothFlip, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 恢复正常方向
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // hFlip=FALSE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_PortraitRestored, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 隐藏头像
setvar VAR_0x8004, 0 // 左侧
special Special_HideDialoguePortrait
// 清理头像系统
special Special_CleanupDialoguePortraitSystem
release
end
// 示例2:双人对话中的翻转效果
EventScript_PortraitFlipDual::
lock
faceplayer
// 初始化头像系统
special Special_InitDialoguePortraitSystem
msgbox Text_DualPortraitIntro, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 显示两个头像
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // 头像ID 0
setvar VAR_0x8006, 1001 // 训练师ID: Brendan
setvar VAR_0x8007, 0 // 调色板标签
special Special_ShowDialoguePortrait
setvar VAR_0x8004, 1 // 右侧
setvar VAR_0x8005, 1 // 头像ID 1
setvar VAR_0x8006, 1000 // 训练师ID: May
setvar VAR_0x8007, 0 // 调色板标签
special Special_ShowDialoguePortrait
// 左侧角色说话(正常面向右边)
msgbox Text_LeftCharacterNormal, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 右侧角色说话(水平翻转面向左边)
setvar VAR_0x8004, 1 // 右侧
setvar VAR_0x8005, 1 // hFlip=TRUE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_RightCharacterFlipped, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 两个角色都面向前方
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // hFlip=FALSE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
setvar VAR_0x8004, 1 // 右侧
setvar VAR_0x8005, 0 // hFlip=FALSE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_BothFacingForward, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 隐藏头像
setvar VAR_0x8004, 0 // 左侧
special Special_HideDialoguePortrait
setvar VAR_0x8004, 1 // 右侧
special Special_HideDialoguePortrait
// 清理头像系统
special Special_CleanupDialoguePortraitSystem
release
end
// 示例3:动态翻转效果(模拟角色转头)
EventScript_PortraitFlipDynamic::
lock
faceplayer
// 初始化头像系统
special Special_InitDialoguePortraitSystem
msgbox Text_DynamicFlipIntro, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 显示头像
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // 头像ID 0
setvar VAR_0x8006, 1000 // 自定义头像ID
setvar VAR_0x8007, 0 // 调色板标签
special Special_ShowDialoguePortrait
// 正常方向(面向右边)
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // hFlip=FALSE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_FacingRight, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
delay 30
// 水平翻转(面向左边)
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 1 // hFlip=TRUE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_FacingLeft, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
delay 30
// 再次翻转回来
setvar VAR_0x8004, 0 // 左侧
setvar VAR_0x8005, 0 // hFlip=FALSE
setvar VAR_0x8006, 0 // vFlip=FALSE
special Special_SetDialoguePortraitFlip
msgbox Text_FacingRightAgain, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
// 隐藏头像
setvar VAR_0x8004, 0 // 左侧
special Special_HideDialoguePortrait
// 清理头像系统
special Special_CleanupDialoguePortraitSystem
msgbox Text_DemoComplete, MSGBOX_DEFAULT
waitmessage
waitbuttonpress
release
end
———————————————–分割线—————————————————
脚本封装更新
因为之前的写法过于臃肿,所以需要改进一下脚本的写法
定义语义化宏常量:constants\portrait_constants.inc
// 头像系统常量定义
// 头像位置常量
#define PORTRAIT_LEFT 0
#define PORTRAIT_RIGHT 1
// 头像翻转常量
#define FLIP_NONE 0
#define FLIP_HORIZONTAL 1
#define FLIP_VERTICAL 1
#define FLIP_BOTH 1
// 头像系统变量别名
#define PORTRAIT_POS VAR_0x8004
#define PORTRAIT_ID VAR_0x8005
#define PORTRAIT_GFX_TAG VAR_0x8006
#define PORTRAIT_PAL_TAG VAR_0x8007
#define PORTRAIT_H_FLIP VAR_0x8008
#define PORTRAIT_V_FLIP VAR_0x8009
#define PORTRAIT_FLIP_H VAR_0x8005
#define PORTRAIT_FLIP_V VAR_0x8006
在这个文件constants\constants.inc新增一行:
.include "constants/portrait_constants.inc"
封装常用操作为宏
// ==================== Portrait System Macros ====================
// Portrait position constants
.set PORTRAIT_LEFT, 0
.set PORTRAIT_RIGHT, 1
// Portrait flip constants
.set FLIP_NONE, 0
.set FLIP_HORIZONTAL, 1
.set FLIP_VERTICAL, 1
.set FLIP_BOTH, 1
// Portrait system variable aliases
.set PORTRAIT_POS, VAR_0x8004
.set PORTRAIT_ID, VAR_0x8005
.set PORTRAIT_GFX_TAG, VAR_0x8006
.set PORTRAIT_PAL_TAG, VAR_0x8007
.set PORTRAIT_H_FLIP, VAR_0x8008
.set PORTRAIT_V_FLIP, VAR_0x8009
.set PORTRAIT_FLIP_H, VAR_0x8005
.set PORTRAIT_FLIP_V, VAR_0x8006
// Show portrait macro
.macro SHOW_PORTRAIT pos, id, gfx_tag, pal_tag
setvar PORTRAIT_POS, \pos
setvar PORTRAIT_ID, \id
setvar PORTRAIT_GFX_TAG, \gfx_tag
setvar PORTRAIT_PAL_TAG, \pal_tag
special Special_ShowDialoguePortrait
.endm
// Show flipped portrait macro
.macro SHOW_PORTRAIT_FLIP pos, id, gfx_tag, pal_tag, h_flip, v_flip
setvar PORTRAIT_POS, \pos
setvar PORTRAIT_ID, \id
setvar PORTRAIT_GFX_TAG, \gfx_tag
setvar PORTRAIT_PAL_TAG, \pal_tag
setvar PORTRAIT_H_FLIP, \h_flip
setvar PORTRAIT_V_FLIP, \v_flip
special Special_ShowDialoguePortraitWithFlip
.endm
// Hide portrait macro
.macro HIDE_PORTRAIT pos
setvar PORTRAIT_POS, \pos
special Special_HideDialoguePortrait
.endm
// Set portrait flip macro
.macro SET_PORTRAIT_FLIP pos, h_flip, v_flip
setvar PORTRAIT_POS, \pos
setvar PORTRAIT_FLIP_H, \h_flip
setvar PORTRAIT_FLIP_V, \v_flip
special Special_SetDialoguePortraitFlip
.endm
改进后的脚本示例:
// ==================== 头像系统测试脚本 ====================
EventScript_PortraitSystemTest::
lock
faceplayer
msgbox Text_PortraitTestIntro, MSGBOX_DEFAULT
call EventScript_PortraitBasicTest
call EventScript_PortraitFlipTest
call EventScript_PortraitCustomTest
call EventScript_PortraitDualTest
call EventScript_PortraitAdvancedTest
msgbox Text_PortraitTestComplete, MSGBOX_DEFAULT
release
end
// 基础功能测试
EventScript_PortraitBasicTest::
msgbox Text_BasicTestStart, MSGBOX_DEFAULT
special Special_InitDialoguePortraitSystem
SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
delay 30
msgbox Text_LeftPortraitShown, MSGBOX_DEFAULT
SHOW_PORTRAIT PORTRAIT_RIGHT, 2, 2, 2
delay 30
msgbox Text_RightPortraitShown, MSGBOX_DEFAULT
HIDE_PORTRAIT PORTRAIT_LEFT
delay 15
msgbox Text_LeftPortraitHidden, MSGBOX_DEFAULT
HIDE_PORTRAIT PORTRAIT_RIGHT
delay 15
msgbox Text_RightPortraitHidden, MSGBOX_DEFAULT
special Special_CleanupDialoguePortraitSystem
return
// 翻转功能测试
EventScript_PortraitFlipTest::
msgbox Text_FlipTestStart, MSGBOX_DEFAULT
special Special_InitDialoguePortraitSystem
SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
delay 30
msgbox Text_NormalPortrait, MSGBOX_DEFAULT
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_NONE
delay 30
msgbox Text_HorizontalFlip, MSGBOX_DEFAULT
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_VERTICAL
delay 30
msgbox Text_VerticalFlip, MSGBOX_DEFAULT
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_VERTICAL
delay 30
msgbox Text_BothFlip, MSGBOX_DEFAULT
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_NONE
delay 30
msgbox Text_NormalAgain, MSGBOX_DEFAULT
HIDE_PORTRAIT PORTRAIT_LEFT
delay 15
SHOW_PORTRAIT_FLIP PORTRAIT_RIGHT, 3, 3, 3, FLIP_HORIZONTAL, FLIP_NONE
delay 30
msgbox Text_DirectFlipPortrait, MSGBOX_DEFAULT
HIDE_PORTRAIT PORTRAIT_RIGHT
delay 15
special Special_CleanupDialoguePortraitSystem
return
// 自定义头像测试
EventScript_PortraitCustomTest::
msgbox Text_CustomTestStart, MSGBOX_DEFAULT
special Special_InitDialoguePortraitSystem
SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1000, 2000
delay 30
msgbox Text_CustomPortrait1, MSGBOX_DEFAULT
SHOW_PORTRAIT_FLIP PORTRAIT_RIGHT, 2, 1001, 2001, FLIP_HORIZONTAL, FLIP_NONE
delay 30
msgbox Text_CustomPortrait2, MSGBOX_DEFAULT
HIDE_PORTRAIT PORTRAIT_LEFT
delay 15
HIDE_PORTRAIT PORTRAIT_RIGHT
delay 15
special Special_CleanupDialoguePortraitSystem
return
// 双头像对话测试
EventScript_PortraitDualTest::
msgbox Text_DualTestStart, MSGBOX_DEFAULT
special Special_InitDialoguePortraitSystem
SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
SHOW_PORTRAIT PORTRAIT_RIGHT, 2, 2, 2
delay 30
msgbox Text_Dialogue1, MSGBOX_DEFAULT
delay 20
HIDE_PORTRAIT PORTRAIT_LEFT
delay 15
msgbox Text_Dialogue2, MSGBOX_DEFAULT
delay 20
SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
delay 15
HIDE_PORTRAIT PORTRAIT_RIGHT
delay 15
msgbox Text_Dialogue3, MSGBOX_DEFAULT
delay 20
SHOW_PORTRAIT PORTRAIT_RIGHT, 2, 2, 2
delay 15
msgbox Text_Dialogue4, MSGBOX_DEFAULT
delay 20
HIDE_PORTRAIT PORTRAIT_LEFT
delay 15
HIDE_PORTRAIT PORTRAIT_RIGHT
delay 15
special Special_CleanupDialoguePortraitSystem
return
// 高级功能测试
EventScript_PortraitAdvancedTest::
msgbox Text_AdvancedTestStart, MSGBOX_DEFAULT
special Special_InitDialoguePortraitSystem
SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
delay 30
msgbox Text_DynamicFlipStart, MSGBOX_DEFAULT
call EventScript_FlipAnimation
call EventScript_TrainerGallery
HIDE_PORTRAIT PORTRAIT_LEFT
delay 15
special Special_CleanupDialoguePortraitSystem
return
// 翻转动画效果
EventScript_FlipAnimation::
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_NONE
delay 20
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_VERTICAL
delay 20
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_VERTICAL
delay 20
SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_NONE
delay 20
return
// 训练师头像画廊
EventScript_TrainerGallery::
SHOW_PORTRAIT PORTRAIT_RIGHT, 10, 10, 10
delay 30
HIDE_PORTRAIT PORTRAIT_RIGHT
delay 15
return
// ====================
// 文本定义
// ====================
Text_PortraitTestIntro::
.string "头像系统测试开始!\n"
.string "将测试所有头像功能。$"
Text_BasicTestStart::
.string "基础功能测试:\n"
.string "测试头像的显示和隐藏。$"
Text_LeftPortraitShown::
.string "左侧头像已显示。\n"
.string "这是训练师的头像。$"
Text_RightPortraitShown::
.string "右侧头像已显示。\n"
.string "这是另一个训练师。$"
Text_LeftPortraitHidden::
.string "左侧头像已隐藏。$"
Text_RightPortraitHidden::
.string "右侧头像已隐藏。\n"
.string "基础测试完成。$"
Text_FlipTestStart::
.string "翻转功能测试:\n"
.string "测试头像的各种翻转效果。$"
Text_NormalPortrait::
.string "这是正常的头像。$"
Text_HorizontalFlip::
.string "现在是水平翻转状态。\n"
.string "头像被左右翻转了。$"
Text_VerticalFlip::
.string "现在是垂直翻转状态。\n"
.string "头像被上下翻转了。$"
Text_BothFlip::
.string "现在是双向翻转状态。\n"
.string "头像同时进行了水平和垂直翻转。$"
Text_NormalAgain::
.string "恢复到正常状态。$"
Text_DirectFlipPortrait::
.string "这是直接显示的翻转头像。\n"
.string "一步到位,无需等待!$"
Text_CustomTestStart::
.string "自定义头像测试:\n"
.string "测试自定义头像资源。$"
Text_CustomPortrait1::
.string "这是自定义头像1。\n"
.string "使用自定义的图形和调色板。$"
Text_CustomPortrait2::
.string "这是自定义头像2。\n"
.string "并且带有水平翻转效果。$"
Text_DualTestStart::
.string "双头像对话测试:\n"
.string "模拟对话场景中的头像切换。$"
Text_Dialogue1::
.string "玩家:你好!\n"
.string "我是这个地区的训练师。$"
Text_Dialogue2::
.string "NPC:欢迎来到我们的城镇!\n"
.string "这里有很多强大的训练师。$"
Text_Dialogue3::
.string "玩家:听起来很有挑战性!\n"
.string "我很期待与他们对战。$"
Text_Dialogue4::
.string "NPC:祝你好运!\n"
.string "如果你需要帮助,随时来找我。$"
Text_AdvancedTestStart::
.string "高级功能测试:\n"
.string "测试动态效果和特殊功能。$"
Text_DynamicFlipStart::
.string "现在演示动态翻转效果:$"
Text_PortraitTestComplete::
.string "所有头像功能测试完成!\n"
.string "系统运行正常。$"
// ====================
// 使用说明
// ====================
/*
使用方法:
1. 将此脚本添加到你的地图脚本文件中
2. 创建一个NPC事件,调用 EventScript_PortraitSystemTest
3. 与NPC对话即可开始测试
测试内容:
- 基础显示/隐藏功能
- 水平/垂直翻转效果
- 自定义头像资源
- 双头像对话场景
- 动态翻转动画
- 训练师头像画廊
注意事项:
- 确保已正确实现头像系统的所有函数
- 自定义头像需要预先准备好图形和调色板资源
- 训练师ID需要根据实际情况调整
- 可以根据需要调整delay的时间长度
*/
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
![[GBA教程]pokeemerald新增NPC头像显示,并支持镜像翻转-宝可梦营地](https://pokedream.cn/wp-content/uploads/2026/01/QQ20260114-144714.png)

![[GBA]宝可梦 彼岸花绽放之夜 v1.0正式版-宝可梦营地](https://pokedream.cn/wp-content/uploads/2026/01/1.png)
![[GBA教程]pokeemerald三层地图块详解-宝可梦营地](https://pokedream.cn/wp-content/uploads/2026/01/QQ20260115-154539.png)
![[GBA教程]pokeemerald新增自定义多项选择框-宝可梦营地](https://pokedream.cn/wp-content/uploads/2026/01/1F90AAF66BB1765251BED3AB10ACDD15.png)
![[GBA教程]pokeemerald动态地图块实操记录-宝可梦营地](https://pokedream.cn/wp-content/uploads/2026/01/QQ20260123-171516.png)

![[NDS中文]宝可梦白2加强v1.0.1测试版-宝可梦营地](https://pokedream.cn/wp-content/uploads/2025/12/pokew2.png)

![[NDS]心金魂银自用debug内置修改器-宝可梦营地](https://pokedream.cn/wp-content/uploads/2026/01/game_patched__13041.png)
![[GBA教程]pokeemerald实现动态地图色板功能-宝可梦营地](https://pokedream.cn/wp-content/uploads/2025/12/LittlerootTown.png)
![[记录]GBA反编译上踩过的一些坑-宝可梦营地](https://pokedream.cn/wp-content/uploads/2025/12/water.jpg)
暂无评论内容