じ☆ve冰风 发表于 2026-5-11 02:33:40

仿Undertale 风格敌人对话气泡

RUBY 代码
#==============================================================================
# ■ Undertale 风格敌人对话气泡
#==============================================================================
#作者:ruigi
#==============================================================================
# ■ 使用说明
#==============================================================================
# 【1】在敌人备注中写入对话标签
#   格式:
#   如:
#   可写入多行,每次随机抽取一句。
#
# 【2】手动换行(仅支持事件脚本)
#   使用标签,例如:
#   自动换行已启用,超宽文字会自动折行。
#
# 【3】事件脚本指令(在战斗事件的[脚本]中使用)
#   set_enemy_dialogue(索引, "台词")   # 为敌人设置自定义对话(下次行动时显示)
#   force_enemy_dialogue(索引)         # 立即强制显示该敌人的对话气泡
#   clear_enemy_dialogue(索引)         # 清除自定义对话,恢复从备注随机读取
#   dismiss_enemy_dialogue             # 立即关闭当前显示的气泡
#
# 【4】可调参数(见模块 Enemy_Dialogue)
#   ENABLED         : 总开关(false 完全关闭功能)
#   TEXT_TYPEWRITER   : 打字机效果开关
#   TYPE_SPEED      : 打字速度(每 N 帧显示一个字)
#   EXTRA_PAUSE      : 打字完成后额外停留帧数
#   DELAY_ACTION   : 是否等气泡消失后再发动攻击
#   SIMULTANEOUS_BUBBLES      : 是否所有敌人同时显示气泡
#   SIMULTANEOUS_BUBBLES_WAIT   : 全体气泡是否等待结束后才继续行动
#   REPEAT_DIALOGUE : 是否每回合都可能说话
#   Z 键(确认键)可跳过当前正在显示的对话(打字或停留)。
#
# 【5】资源文件
#   请在 Graphics/System/ 下放置气泡图片,文件名见 DIALOGUE_BUBBLE_IMAGE。
#==============================================================================
module Enemy_Dialogue
ENABLED                = true
DIALOGUE_BUBBLE_IMAGE= "Enemy_Dialogue_Bubble"
DURATION_IN_FRAMES   = 200

X_OFFSET   = -30
Y_OFFSET   = -290
Z_VALUE    = 100

TEXT_COLOR      = Color.new(255, 255, 255, 255)
TEXT_SIZE         = 20
TEXT_BOLD         = true
TEXT_SHADOW_COLOR = Color.new(0, 0, 0, 160)

TEXT_TYPEWRITER= true
TYPE_SPEED       = 2
EXTRA_PAUSE      = 40

AUTO_WRAP = true
MANUAL_BREAK_TAG = //i
TEXT_PADDING_LEFT= 12
TEXT_PADDING_RIGHT = 12
LINE_SPACING = 24

BUBBLE_ZOOM_X= 1.0
BUBBLE_ZOOM_Y= 1.0
SHOW_IN_LOG    = false

REPEAT_DIALOGUE = true
DELAY_ACTION    = true
SIMULTANEOUS_BUBBLES      = true
SIMULTANEOUS_BUBBLES_WAIT = true
end

#==============================================================================
# ■ RPG::Enemy
#==============================================================================
classRPG::Enemy < RPG::BaseItem
def dialogue_lines
    @dialogue_lines ||= load_dialogue_lines
end

def random_dialogue
    lines = dialogue_lines
    lines.empty? ? nil : lines
end

private

def load_dialogue_lines
    lines = []
    note.scan(//i)do |match|
      lines.push(match.strip)unless match.strip.empty?
    end
    lines
end
end

#==============================================================================
# ■ Game_Enemy
#==============================================================================
class Game_Enemy < Game_Battler
attr_accessor :custom_dialogue_text
attr_accessor :dialogue_displayed
attr_accessor :just_spoke_in_group

alias enemy_dialogue_initialize initialize
def initialize(index, enemy_id)
    enemy_dialogue_initialize(index, enemy_id)
    @custom_dialogue_text = nil
    @dialogue_displayed   = false
    @just_spoke_in_group= false
end

def active_dialogue_text
    return@custom_dialogue_textif@custom_dialogue_text
    enemy.random_dialogue
end

def has_dialogue?
    returntrueif@custom_dialogue_text
    enemy.dialogue_lines.any?
end

def custom_dialogue_active?
    !@custom_dialogue_text.nil?
end
end

#==============================================================================
# ■ Window_Enemy_Dialogue_Bubble
#==============================================================================
class Window_Enemy_Dialogue_Bubble < Window_Base
attr_reader :enemy, :text
attr_reader :typing_finished

def initialize(enemy, raw_text)
    @enemy = enemy
    @raw_text = raw_text
    @processed_text = @raw_text.gsub(Enemy_Dialogue::MANUAL_BREAK_TAG, "\n")
    @char_index = 0
    @type_timer = 0
    @typing_finished = false

    super(0, 0, 210, 80)
    self.opacity          = 0
    self.back_opacity   = 0
    self.contents_opacity = 255
    self.z = Enemy_Dialogue::Z_VALUE

    create_contents
    max_width = max_text_width
    @total_lines = calculate_lines(@processed_text, max_width)
    line_height = Enemy_Dialogue::LINE_SPACING
    w = .min].max
    h = .max

    self.width = w
    self.height = h
    create_contents

    create_bubble_sprite
    update_display_text
    set_position
end

# 自动换行计算
def full_text_width
    bmp = Bitmap.new(1, 1)
    bmp.font.size = Enemy_Dialogue::TEXT_SIZE
    bmp.font.bold = Enemy_Dialogue::TEXT_BOLD
    w = bmp.text_size(@processed_text).width
    bmp.dispose
    w
end

def max_text_width
    self.width - 32 - Enemy_Dialogue::TEXT_PADDING_LEFT - Enemy_Dialogue::TEXT_PADDING_RIGHT
end

def create_bubble_sprite
    @bubble_sprite = Sprite.new
    @bubble_sprite.bitmap = Cache.system(Enemy_Dialogue::DIALOGUE_BUBBLE_IMAGE)
    @bubble_sprite.z      = self.z - 100
    @bubble_sprite.zoom_x = Enemy_Dialogue::BUBBLE_ZOOM_X
    @bubble_sprite.zoom_y = Enemy_Dialogue::BUBBLE_ZOOM_Y
end

def create_contents
    self.contents.disposeifself.contents
    self.contents = Bitmap.new(width - 32, height - 32)
    self.contents.clear
end

def calculate_lines(text, max_width)
    lines = []
    paragraphs = text.split("\n")
    bmp = self.contents
    bmp.font.size = Enemy_Dialogue::TEXT_SIZE
    bmp.font.bold = Enemy_Dialogue::TEXT_BOLD

    paragraphs.eachdo |para|
      if para.empty?
      linesmax_width
          lines
页: [1]
查看完整版本: 仿Undertale 风格敌人对话气泡