找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 85|回复: 0

[教程] 极简手机聊天信息系统(纯代码,新手也能用)

[复制链接]
发表于 昨天 01:36 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 Maz马 于 2026-1-14 00:40 编辑

手机聊天信息是老东西了,做的人很多
这个是很早之前黑凤梨老师推荐我看的,得有两年了吧,希望代码不会过时(
原帖出处:
https://nighten.itch.io/yet-another-phone-renpy


但是原帖是一个工程包,相当杂乱,而且布局用的都是绝对数据,很难适配
刚好最近帮人写用到了,我就,来都来了,那就顺手(

提取了参数,方便小白使用,我相信东一榔头西一棒槌的布局是大家最头疼的...

另外写成了可以群聊,和有缩略图功能,会screen的也可以自行扩展
(一如既往,由于基本框架不是我的,所以投教程)
(我真是@Koji 一手带大,这个很早之前他也帮过忙)

第一部分 系统
[RenPy] 纯文本查看 复制代码
### 布局变量 ############################################################################

default phone_fg = "phone_fg_00"              # 用于实时反馈手机外框的变量
image phone_fg_00 = "phone_fg_00.png"         # 手机界面的外框

default phone_bg = "phone_bg_00"              # 用于实时反馈手机背景的变量
image phone_bg_00 = "phone_bg_00.png"         # 手机界面的背景

default phone_s_frame = "phone_s_frame_00"    # 用于实时反馈发信者气泡的变量
image phone_s_frame:                          # 发信者的气泡
    "#fff"

default phone_r_frame = "phone_r_frame_00"    # 用于实时反馈收信者的气泡的变量
image phone_r_frame:                          # 收信者的气泡
    "#777"

default phone_n_color = "#777"              # 旁白文本颜色
default phone_s_color = "#FFF"              # 发信者文本颜色
default phone_r_color = "#000"              # 收信者文本颜色

define audio.phonesend = "sendtext.mp3"       # 用于发送信息的音频
define audio.phonereceive = "receivetext.mp3" # 用于接收信息的音频

default phone_xy = (0.5,0.5)                  # 手机在屏幕上的坐标
default phone_xysize = (900,1800)             # 手机界面的尺寸(就是外框的尺寸)

default p_frame_xysize = (890,1500)           # 聊天窗口的尺寸,一般略小于手机(就是比外框略小)
default p_frame_spacing = 10                  # 聊天消息之间的上下间隔

default p_narrator_width = 580                # 旁白消息的最大宽度
default p_narrator_textsize = 34              # 旁白的字体大小

default p_message_width = 580                 # 普通消息的最大宽度
default p_message_padding = (20,10,20,15)     # 消息文本到气泡框架的左上右下边距
default p_message_textsize = 34               # 消息的字体大小
default p_message_xoffset = 20                # 消息距离侧方边界的偏移

### 系统变量 #############################################################################

default nvl_mode = "phone"                    # 区分正常NVL模式和手机组件的变量
define config.nvl_list_length = 999           # 最大对话记录数(在gui中也存在一条,这里直接覆写)

### 以下所有都不需要会,只需要调整上面的参数 ########################################################

init -1 python:
    #可入参的showscreen文本标签
    def show_screen_handler(target):
        if "," in target:
            screen_name, params_str = target.split(",", 1)
            params = {}
            for pair in params_str.split(","):
                if "=" in pair:
                    key, val = pair.split("=", 1)
                    params[key.strip()] = val.strip()
            renpy.show_screen(screen_name, **params)
        else:
            renpy.show_screen(target)
        return True

    config.hyperlink_handlers["show"] = show_screen_handler

    def showscreen_tag(tag, argument, contents):
        return [
            (renpy.TEXT_TAG, f"a=show:{argument}"),
            *contents,
            (renpy.TEXT_TAG, "/a")
        ]

    config.custom_text_tags["showscreen"] = showscreen_tag

    # 接收消息时的音效处理函数
    def Phone_ReceiveSound(event,interact=True,**kwargs):
        if event == "show_done":
            renpy.sound.play(audio.phonereceive)
        #global current_speaker 无视,被我优化掉了
        if not interact:
            return
        #if event == "begin":
        #    current_speaker = name
    
    # 发送消息时的音效处理函数        
    def Phone_SendSound(event,interact=True, **kwargs):
        if event == "show_done":
            renpy.sound.play(audio.phonesend)

# 消息动画效果(左右弹出,1为右,-1为左)
transform message_appear(pDirection):
    alpha 0.0
    xoffset 50 * pDirection
    parallel:
        ease 0.5 alpha 1.0
    parallel:
        easein_back 0.5 xoffset 0
# 头像动画效果(快速放大)
transform message_appear_icon():
    zoom 0.0
    easein_back 0.5 zoom 1.0
# 旁白动画效果(向上弹出)
transform message_narrator:
    alpha 0.0
    yoffset -50
    parallel:
        ease 0.5 alpha 1.0
    parallel:
        easein_back 0.5 yoffset 0

# 一个用于聊天中缩略图放大功能的界面
screen phone_cg(phonecg):
    zorder 102
    modal True
    fixed:
        anchor (0.5,0.5)
        xysize phone_xysize
        pos phone_xy
        add phone_bg align (0.5,0.5)
        imagebutton:
            align (0.5,0.5)
            idle phonecg
            action Hide("phone_cg")
        add phone_fg align (0.5,0.5)
# 手机对话界面主屏幕
screen PhoneDialogue(dialogue,items=None):
    zorder 101
    fixed:
        anchor (0.5,0.5)
        xysize phone_xysize
        pos phone_xy
        fixed:  # 消息显示区域
            align (0.5,0.5)
            xysize p_frame_xysize
            add phone_bg align (0.5,0.5)
            viewport:  # 可滚动区域
                align (0.5,0.5)
                draggable True
                mousewheel True
                yinitial 1.0  # 跟踪底部
                vbox:
                    xalign 0.5
                    spacing p_frame_spacing
                    xsize p_frame_xysize[0]
                    use nvl_phonetext(dialogue)  # 调用消息显示组件
                    # 如果有选项的话
                    if items != None:
                        vbox:
                            xalign 1.0
                            for i in items:
                                textbutton i.caption:
                                    background Frame(phone_s_frame,30,30,30,30) at message_narrator
                                    padding p_message_padding
                                    xsize p_message_width
                                    text_size p_message_textsize
                                    text_xalign 0.5
                                    action i.action
                            null height 50
        add phone_fg align (0.5,0.5)
# 手机消息显示组件
screen nvl_phonetext(dialogue):
    zorder 101
    # 初始化 上一条消息发送者
    $ previous_d_who = None
    # 遍历所有对话
    for id_d,d in enumerate(dialogue):
        # 旁白消息
        if d.who == None:
            frame:
                xalign 0.5
                background None
                text d.what:
                    xalign 0.5
                    xsize p_narrator_width
                    text_align 0.5
                    color phone_n_color
                    size p_narrator_textsize
                    italic True
                    slow_cps False
                    id d.what_id
                    # 只有当前消息才会应用动画
                    if d.current:
                        at message_narrator
        # 角色消息
        else:
            if d.who == mc_nvl.name:  # 判断是发送还是接收的消息框,从字典获取头像
                $ message_frame = phone_s_frame
                $ message_icon = phone_avatars[mc_nvl.name]
            else:
                $ message_frame = phone_r_frame
                $ message_icon = phone_avatars.get(d.who,phone_avatars["default"])
            hbox:
                if d.who == mc_nvl.name:
                    box_reverse True
                    xalign 1.0 xoffset -p_message_xoffset
                else:
                    xalign 0.0 xoffset p_message_xoffset
                spacing 10
                add message_icon:  # 添加头像
                    if d.current:  # 只有当前消息才会应用动画
                        at message_appear_icon()
                vbox:
                    yalign 1.0
                    #if d.who != mc_nvl.name and previous_d_who != d.who:
                    #取消注释添加缩进:玩家不显示姓名,同时,只在新角色说话时才显示姓名
                    text d.who:
                        if d.who == mc_nvl.name:
                            textalign 1.0
                            xalign 1.0
                    # 消息气泡
                    frame:
                        background Frame(message_frame,30,30,30,30)
                        padding p_message_padding
                        xsize p_message_width
                        if d.current:                  # 只有当前消息才会应用动画
                            if d.who == mc_nvl.name:
                                at message_appear(1)   # 发送消息从右出现
                            else:
                                at message_appear(-1)  # 接收消息从左出现
                        # 消息文本
                        text d.what:
                            pos (0,0)
                            xsize p_message_width
                            size p_message_textsize
                            slow_cps False
                            if d.who == mc_nvl.name:
                                color phone_s_color
                                textalign 1.0
                                xalign 1.0
                            else:
                                color phone_r_color
                            id d.what_id
        # 更新 上一条消息发送者
        $ previous_d_who = d.who


第二部分 使用方法
[RenPy] 纯文本查看 复制代码
# 测试用图片-头像
image phone tx01:
    Solid("#0b0")
    xysize(30,30)
image phone tx02:
    Solid("#b00")
    xysize(30,30)
image phone tx03:
    Solid("#000")
    xysize(30,30)
# 测试用图片-可放大的CG
image phone cg01:
    Solid("#000")
    xysize(300,300)
image phone cg01ex:
    Solid("#000")
    xysize(608,1080)
image phone cg02:
    Solid("#000")
    xysize(300,300) 
image phone cg02ex:
    xysize(608,1080)
    Solid("#000")
    pause 0.5
    Solid("#00f")
    pause 0.5
    Solid("#0ff")
    pause 0.5
    Solid("#fff")
    pause 0.5
    repeat
# 定义角色

# 写法如下,只是增加了kind=nvl参数,然后callback=Phone_SendSound/ReceiveSound(发信角色/收信角色),然后image=头像路径,找规律填应该不难
define mc_nvl = Character("Nighten",kind=nvl,callback=Phone_SendSound,image="phone tx01")

define niko_nvl = Character("妮可",kind=nvl,callback=Phone_ReceiveSound,image="phone tx02")
define ll_nvl = Character("lovelive",kind=nvl,callback=Phone_ReceiveSound,image="phone tx03")

# 头像列表,由于这个系统读取的不是实例,而是记录了字符串,所以要多写一个列表来找到头像
default phone_avatars = {
    renpy.translate_string(mc_nvl.name):mc_nvl.image_tag,

    renpy.translate_string(niko_nvl.name):niko_nvl.image_tag,
    renpy.translate_string(ll_nvl.name):ll_nvl.image_tag,

    # 写法就是renpy.translate_string(角色变量名.name):角色变量名.image_tag,找规律填应该不难
    # 默认头像(未匹配时使用)
    "default":mc_nvl.image_tag}

label start:
    "开始"
    call phonelabel_01
    "现在,你完成了"
    return

# 如何写一个手机信息事件:
label phonelabel_01:
    $ nvl_mode = "phone" # 先打开手机模式
    nvl_narrator "这是旁白的写法"
    niko_nvl "使用专用的角色开始对话"
    niko_nvl "这样插入一张 emoji 或 小图{image=phone/emoji/clap.png}"
    menu(nvl=True):
        nvl_narrator "选项菜单要入参 nvl=True"
        "选项1":
            pass
        "选项2":
            pass
    mc_nvl "这是另一边的角色在说话"
    niko_nvl "然后是 一个可放大的图像,点击图像将会放大,但形状要自己裁切"
    niko_nvl "{showscreen=phone_cg,phonecg=phone cg01ex}{image=phone cg01}{/showscreen}"
    niko_nvl "phonecg=phone cg01ex 是放大的图片,image=phone cg01 是缩略图"
    niko_nvl "然后是 一个可播放的图像,点击图像将会放大,且开始播放"
    niko_nvl "{showscreen=phone_cg,phonecg=phone cg02ex}{image=phone cg02}{/showscreen}"
    niko_nvl "phonecg=phone cg02ex 是放大的帧动画,image=phone cg02 是缩略图"
    mc_nvl "现在尝试互动,这是右边的角色在说话"
    ll_nvl "现在尝试跳过对话"
    # interact=False 这个参数似乎可以用来制作 “被阅读过的消息记录”,也就是不需要从头点到尾
    # 但暂时没时间研究is_seen相关的代码,先插眼,应该不难写,有大神也可以留言
    ll_nvl "这句话不需要点击会自己跳过"(interact=False)
    ll_nvl "现在尝试多角色对话"
    niko_nvl "这是左边的 [niko_nvl] 在说话"
    ll_nvl "这是左边的 [ll_nvl] 在说话"
    nvl clear # 清空nvl界面,释放缓存
    $ nvl_mode = None # 关闭手机模式(养成封闭习惯)
    return


第三部分 修改
以上可以直接建成新文件,以下是需要在源文件screen.rpy中修改的,只加一句话
[RenPy] 纯文本查看 复制代码
screen nvl(dialogue, items=None):
    zorder 101
    ### 在这里增加应用手机系统的分支 ###
    if nvl_mode == "phone":
        use PhoneDialogue(dialogue,items)
    else:
    ### 以下是源代码只增加了缩进没有任何改动 ###
        window:
            style "nvl_window"

            has vbox:
                spacing gui.nvl_spacing

            ## 在 vpgrid 或 vbox 中显示对话框。
            if gui.nvl_height:

                vpgrid:
                    cols 1
                    yinitial 1.0

                    use nvl_dialogue(dialogue)

            else:

                use nvl_dialogue(dialogue)

            ## 显示菜单,如果给定的话。如果 config.narrator_menu 设置为 True,则菜单
            ## 可能显示不正确。
            for i in items:

                textbutton i.caption:
                    action i.action
                    style "nvl_button"

    add SideImage() xalign 0.0 yalign 1.0



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2026-1-14 09:45 , Processed in 0.043721 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表