NPC交互设计(对话)

全部demo源自于here

所有设计依赖加载插件rexuiplugin

  • Install rex plugins from npm  npm i phaser3-rex-plugins

 

相关封装对象有,BBCodeCustomshapes

1.Dialog类型,实现全屏点击出现对话框,点击choice后,在点击位置留下选中choice

 参考代码:

复制代码
class Demo extends Phaser.Scene {
    constructor() {
        super({
            key: 'examples'
        })
    }

    preload() { 
        this.load.scenePlugin({
            key: 'rexuiplugin',
            url: '../../rexuiplugin.min.js',
            sceneKey: 'rexUI'
        });
    }

    create() {
        this.print = this.add.text(0, 580, 'Click to pop-up dialog');

        var scene = this,
            dialog = undefined;
        this.input.on('pointerdown', function (pointer) {
            var x = pointer.x,
                y = pointer.y;

            if (dialog === undefined) {
                // 没有对话文本则新建
                dialog = createDialog(this, x, y, function (color) {
                    scene.add.circle(x, y, 20, color);
                    scene.print.text = 'Add object at (' + x + ',' + y + ')';
                    // 渐变消失
                    dialog.scaleDownDestroy(100);
                    dialog = undefined;
                });
                scene.print.text = 'Click (' + x + ',' + y + ')';
            } else if (!dialog.isInTouching(pointer)) {
                // 有则销毁已有
                dialog.scaleDownDestroy(100);
                dialog = undefined;
            }
        }, this);
    }

    update() {}
}

var createDialog = function (scene, x, y, onClick) {
    var dialog = scene.rexUI.add.dialog({
            x: x,
            y: y,

            // 背景
            background: scene.rexUI.add.roundRectangle(0, 0, 100, 100, 20, 0xf57f17),

            // 对话文本
            title: scene.rexUI.add.label({
                background: scene.rexUI.add.roundRectangle(0, 0, 100, 40, 20, 0xbc5100),
                text: scene.add.text(0, 0, 'Pick a color', {
                    fontSize: '20px'
                }),
                space: {
                    left: 15,
                    right: 15,
                    top: 10,
                    bottom: 10
                }
            }),

            actions: [
                scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0xe91e63),
                scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0x673ab7),
                scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0x2196f3),
                scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0x00bcd4),
                scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0x4caf50),
                scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0xcddc39),
            ],

            actionsAlign: 'left',

            space: {
                title: 10,
                action: 5,

                left: 10,
                right: 10,
                top: 10,
                bottom: 10,
            }
        })
        .layout()
        .pushIntoBounds()
        //.drawBounds(this.add.graphics(), 0xff0000)
        .popUp(500);

    dialog
        .on('button.click', function (button, groupName, index) {
            onClick(button.fillColor);
        })
        .on('button.over', function (button, groupName, index) {
            button.setStrokeStyle(2, 0xffffff);
        })
        .on('button.out', function (button, groupName, index) {
            button.setStrokeStyle();
        });

    return dialog;
}

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Demo
};

var game = new Phaser.Game(config);
View Code
复制代码

2.Dialog类型,实现选项对话。

 参考代码:

复制代码
const GetValue = Phaser.Utils.Objects.GetValue;

const data = {
    title: 'Question 1',
    content: '1 + 1 + 1 + 1 = ',
    choices: [3, 4, 5],
    actions: ['OK', 'Cancel']
};


class Demo extends Phaser.Scene {
    constructor() {
        super({
            key: 'examples'
        })
    }

    preload() { 
        this.load.scenePlugin({
            key: 'rexuiplugin',
            url: '../../rexuiplugin.min.js',
            sceneKey: 'rexUI'
        });      
    }

    create() {
        this.print = this.add.text(0, 0, '');

        var dialog = createDialog(this)
            .layout()
            .on('button.click', function (button, groupName, index, pointer, event) {
                this.print.text += index + ': ' + button.text + '\n';
            }, this)
            .on('button.over', function (button, groupName, index, pointer, event) {
                button.getElement('background').setStrokeStyle(1, 0xffffff);
            })
            .on('button.out', function (button, groupName, index, pointer, event) {
                button.getElement('background').setStrokeStyle();
            })

        this.input.once('pointerdown', function () {
            setDialog(dialog, data).layout();
        });

        this.add.text(0, 580, 'Click.once to reset buttons');
    }

    update() { }
}

var createDialog = function (scene) {
    return scene.rexUI.add.dialog({
        x: 400,
        y: 300,
        width: 360,

        background: scene.rexUI.add.roundRectangle(0, 0, 100, 100, 20, 0x3e2723),

        title: scene.rexUI.add.label({
            background: scene.rexUI.add.roundRectangle(0, 0, 100, 40, 20, 0x1b0000),
            text: scene.add.text(0, 0, ' ', {
                fontSize: '24px'
            }),
            space: {
                left: 15,
                right: 15,
                top: 10,
                bottom: 10
            }
        }),

        content: scene.add.text(0, 0, ' ', {
            fontSize: '24px'
        }),

        choices: [
            createLabel(scene, ' ', 0x6a4f4b),
            createLabel(scene, ' ', 0x6a4f4b),
            createLabel(scene, ' ', 0x6a4f4b),
            createLabel(scene, ' ', 0x6a4f4b),
            createLabel(scene, ' ', 0x6a4f4b)
        ], // Support 5 choices

        actions: [
            createLabel(scene, '', 0x1b0000),
            createLabel(scene, '', 0x1b0000),
            createLabel(scene, '', 0x1b0000)
        ], // Support 3 actions

        space: {
            title: 25,
            content: 25,
            choices: 20,
            choice: 15,
            action: 15,

            left: 25,
            right: 25,
            top: 25,
            bottom: 25,
        },

        expand: {
            content: false,  // Content is a pure text object
        }
    });
}
var createLabel = function (scene, text, backgroundColor) {
    return scene.rexUI.add.label({
        background: scene.rexUI.add.roundRectangle(0, 0, 100, 40, 20, backgroundColor),

        text: scene.add.text(0, 0, text, {
            fontSize: '24px'
        }),

        space: {
            left: 10,
            right: 10,
            top: 10,
            bottom: 10
        }
    });
}

var setDialog = function (dialog, config) {
    // Set title
    dialog.getElement('title').text = GetValue(config, 'title', ' ');
    // Set content
    dialog.getElement('content').text = GetValue(config, 'content', ' ');
    // Set choices
    var choiceTextArray = GetValue(config, 'choices', []),
        choiceText;
    var choices = dialog.getElement('choices');
    for (var i = 0, cnt = choices.length; i < cnt; i++) {
        choiceText = choiceTextArray[i];
        if (choiceText != null) {
            dialog.showChoice(i);
            choices[i].text = choiceText;
        } else {
            dialog.hideChoice(i);
        }
    }
    // Set actions
    var actionTextArray = GetValue(config, 'actions', []),
        actionText;
    var actions = dialog.getElement('actions');
    for (var i = 0, cnt = actions.length; i < cnt; i++) {
        actionText = actionTextArray[i];
        if (actionText != null) {
            dialog.showAction(i);
            actions[i].text = actionText;
        } else {
            dialog.hideAction(i);
        }
    }
    return dialog;
}

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Demo
};

var game = new Phaser.Game(config);
View Code
复制代码

 

3.Dialog类型,实现yes/no双选择对话。

 参考代码:

复制代码
class Demo extends Phaser.Scene {
    constructor() {
        super({
            key: 'examples'
        })
    }

    preload() { 
        this.load.scenePlugin({
            key: 'rexuiplugin',
            url: '../rexuiplugin.min.js',
            sceneKey: 'rexUI'
        });
      
        this.load.image('classroom', '../../home.jpg');
    }

    create() {
        var print = this.add.text(0, 0, '').setDepth(1);

        this.add.image(400, 300, 'classroom')
            .setInteractive()
            .on('pointerup', function () {
                print.text += 'Click bottom image\n';
            })

        this.rexUI.modalPromise(
            // Game object
            CreateDialog(this).setPosition(400, 300),
            // Config
            {
                manaulClose: true,
                duration: {
                    in: 500,
                    out: 500
                }
            }
        )
            .then(function (result) {
                print.text += `Click button ${result.index}: ${result.text}\n`;
            })
    }

    update() { }
}

var CreateDialog = function (scene) {
    var dialog = scene.rexUI.add.dialog({
        background: scene.rexUI.add.roundRectangle(0, 0, 100, 100, 20, 0x1565c0),

        title: scene.rexUI.add.label({
            background: scene.rexUI.add.roundRectangle(0, 0, 100, 40, 20, 0x003c8f),
            text: scene.add.text(0, 0, 'Title', {
                fontSize: '24px'
            }),
            space: {
                left: 15,
                right: 15,
                top: 10,
                bottom: 10
            }
        }),

        content: scene.add.text(0, 0, 'Do you want to build a snow man?', {
            fontSize: '24px'
        }),

        actions: [
            CreateLabel(scene, 'Yes'),
            CreateLabel(scene, 'No')
        ],

        space: {
            title: 25,
            content: 25,
            action: 15,

            left: 20,
            right: 20,
            top: 20,
            bottom: 20,
        },

        align: {
            actions: 'right', // 'center'|'left'|'right'
        },

        expand: {
            content: false,  // Content is a pure text object
        }
    })
        .layout();

    dialog
        .on('button.click', function (button, groupName, index, pointer, event) {
            dialog.emit('modal.requestClose', { index: index, text: button.text });
        })
        .on('button.over', function (button, groupName, index, pointer, event) {
            button.getElement('background').setStrokeStyle(1, 0xffffff);
        })
        .on('button.out', function (button, groupName, index, pointer, event) {
            button.getElement('background').setStrokeStyle();
        });

    return dialog;
}

var CreateLabel = function (scene, text) {
    return scene.rexUI.add.label({
        // width: 40,
        // height: 40,

        background: scene.rexUI.add.roundRectangle(0, 0, 0, 0, 20, 0x5e92f3),

        text: scene.add.text(0, 0, text, {
            fontSize: '24px'
        }),

        space: {
            left: 10,
            right: 10,
            top: 10,
            bottom: 10
        }
    });
}

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Demo
};

var game = new Phaser.Game(config);
View Code
复制代码

 

4.textBox类型,剧情说明对话(固定上边界位置)。

 参考代码:

复制代码
const COLOR_PRIMARY = 0x4e342e;
const COLOR_LIGHT = 0x7b5e57;
const COLOR_DARK = 0x260e04;

var content = `Phaser is a fast, free, and fun open source HTML5 game framework that offers WebGL and Canvas rendering across desktop and mobile web browsers. Games can be compiled to iOS, Android and native apps by using 3rd party tools. You can use JavaScript or TypeScript for development.`;

class Demo extends Phaser.Scene {
    constructor() {
        super({
            key: "examples"
        });
    }

    preload() {
        this.load.scenePlugin({
            key: "rexuiplugin",
            url: "./rexuiplugin.min.js",
            sceneKey: "rexUI"
        });

        this.load.image(
            "nextPage",
            "../../resource/apple_20220918.png"
        );
    }

    create() {
        createTextBox(this, 100, 100, {
            wrapWidth: 500
        }).start(content, 50);

        createTextBox(this, 100, 400, {
            wrapWidth: 500,
            fixedWidth: 500,
            fixedHeight: 65
        }).start(content, 50);
    }

    update() {}
}

const GetValue = Phaser.Utils.Objects.GetValue;
var createTextBox = function (scene, x, y, config) {
    var wrapWidth = GetValue(config, "wrapWidth", 0);
    var fixedWidth = GetValue(config, "fixedWidth", 0);
    var fixedHeight = GetValue(config, "fixedHeight", 0);
    var textBox = scene.rexUI.add
        .textBox({
            x: x,
            y: y,

            background: scene.rexUI.add
                .roundRectangle(0, 0, 2, 2, 20, COLOR_PRIMARY)
                .setStrokeStyle(2, COLOR_LIGHT),

            icon: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 20, COLOR_DARK),

            // text: getBuiltInText(scene, wrapWidth, fixedWidth, fixedHeight),
            text: getBBcodeText(scene, wrapWidth, fixedWidth, fixedHeight),

            action: scene.add
                .image(0, 0, "nextPage")
                .setTint(COLOR_LIGHT)
                .setVisible(false),

            space: {
                left: 20,
                right: 20,
                top: 20,
                bottom: 20,
                icon: 10,
                text: 10
            }
        })
        .setOrigin(0)
        .layout();

    textBox
        .setInteractive()
        .on(
            "pointerdown",
            function () {
                var icon = this.getElement("action").setVisible(false);
                this.resetChildVisibleState(icon);
                if (this.isTyping) {
                    this.stop(true);
                } else {
                    this.typeNextPage();
                }
            },
            textBox
        )
        .on(
            "pageend",
            function () {
                if (this.isLastPage) {
                    return;
                }

                var icon = this.getElement("action").setVisible(true);
                this.resetChildVisibleState(icon);
                icon.y -= 30;
                var tween = scene.tweens.add({
                    targets: icon,
                    y: "+=30", // '+=100'
                    ease: "Bounce", // 'Cubic', 'Elastic', 'Bounce', 'Back'
                    duration: 500,
                    repeat: 0, // -1: infinity
                    yoyo: false
                });
            },
            textBox
        );
    //.on('type', function () {
    //})

    return textBox;
};

var getBuiltInText = function (scene, wrapWidth, fixedWidth, fixedHeight) {
    return scene.add
        .text(0, 0, "", {
            fontSize: "20px",
            wordWrap: {
                width: wrapWidth
            },
            maxLines: 3
        })
        .setFixedSize(fixedWidth, fixedHeight);
};

var getBBcodeText = function (scene, wrapWidth, fixedWidth, fixedHeight) {
    return scene.rexUI.add.BBCodeText(0, 0, "", {
        fixedWidth: fixedWidth,
        fixedHeight: fixedHeight,

        fontSize: "20px",
        wrap: {
            mode: "word",
            width: wrapWidth
        },
        maxLines: 3
    });
};

var config = {
    type: Phaser.AUTO,
    parent: "phaser-example",
    width: 800,
    height: 600,
    scale: {
        mode: Phaser.Scale.FIT,
        autoCenter: Phaser.Scale.CENTER_BOTH
    },
    scene: Demo
};

var game = new Phaser.Game(config);
View Code
复制代码

5.textBox类型,剧情说明对话(固定下边界位置)

 参考代码:

复制代码
const COLOR_PRIMARY = 0x4e342e;
const COLOR_LIGHT = 0x7b5e57;
const COLOR_DARK = 0x260e04;

var content = `Phaser is a fast, free, and fun open source HTML5 game framework that offers WebGL and Canvas rendering across desktop and mobile web browsers. Games can be compiled to iOS, Android and native apps by using 3rd party tools. You can use JavaScript or TypeScript for development.`;

class Demo extends Phaser.Scene {
    constructor() {
        super({
            key: 'examples'
        })
    }

    preload() { 
        this.load.scenePlugin({
            key: 'rexuiplugin',
            url: './rexuiplugin.min.js',
            sceneKey: 'rexUI'
        });
 
        this.load.image('nextPage', '../../resource/apple_20220918.png');
    }

    create() {
        createTextBox(this, 100, 200, {
                wrapWidth: 500,
            })
            .start(content, 50);

        createTextBox(this, 100, 500, {
                wrapWidth: 500,
                fixedWidth: 500,
                fixedHeight: 65,
            })
            .start(content, 50);
    }

    update() {}
}


const GetValue = Phaser.Utils.Objects.GetValue;
var createTextBox = function (scene, x, y, config) {
    var wrapWidth = GetValue(config, 'wrapWidth', 0);
    var fixedWidth = GetValue(config, 'fixedWidth', 0);
    var fixedHeight = GetValue(config, 'fixedHeight', 0);
    var textBox = scene.rexUI.add.textBox({
            x: x,
            y: y,

            background: CreateSpeechBubbleShape(scene)
                .setFillStyle(COLOR_PRIMARY, 1)
                .setStrokeStyle(2, COLOR_LIGHT, 1),

            icon: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 20, COLOR_DARK),

            // text: getBuiltInText(scene, wrapWidth, fixedWidth, fixedHeight),
            text: getBBcodeText(scene, wrapWidth, fixedWidth, fixedHeight),

            action: scene.add.image(0, 0, 'nextPage').setTint(COLOR_LIGHT).setVisible(false),

        space: {
            left: 10, right: 10, top: 10, bottom: 25,
            icon: 10,
            text: 10,
        }
        })
        .setOrigin(0, 1)
        .layout();

    textBox
        .setInteractive()
        .on('pointerdown', function () {
            var icon = this.getElement('action').setVisible(false);
            this.resetChildVisibleState(icon);
            if (this.isTyping) {
                this.stop(true);
            } else {
                this.typeNextPage();
            }
        }, textBox)
        .on('pageend', function () {
            if (this.isLastPage) {
                return;
            }

            var icon = this.getElement('action').setVisible(true);
            this.resetChildVisibleState(icon);
            icon.y -= 30;
            var tween = scene.tweens.add({
                targets: icon,
                y: '+=30', // '+=100'
                ease: 'Bounce', // 'Cubic', 'Elastic', 'Bounce', 'Back'
                duration: 500,
                repeat: 0, // -1: infinity
                yoyo: false
            });
        }, textBox)
    //.on('type', function () {
    //})

    return textBox;
}

var getBuiltInText = function (scene, wrapWidth, fixedWidth, fixedHeight) {
    return scene.add.text(0, 0, '', {
            fontSize: '20px',
            wordWrap: {
                width: wrapWidth
            },
            maxLines: 3
        })
        .setFixedSize(fixedWidth, fixedHeight);
}

var getBBcodeText = function (scene, wrapWidth, fixedWidth, fixedHeight) {
    return scene.rexUI.add.BBCodeText(0, 0, '', {
        fixedWidth: fixedWidth,
        fixedHeight: fixedHeight,

        fontSize: '20px',
        wrap: {
            mode: 'word',
            width: wrapWidth
        },
        maxLines: 3
    })
}

var CreateSpeechBubbleShape = function (scene, fillColor, strokeColor) {
    return scene.rexUI.add.customShapes({
        create: { lines: 1 },
        update: function () {
            var radius = 20;
            var indent = 15;

            var left = 0, right = this.width,
                top = 0, bottom = this.height, boxBottom = bottom - indent;
            this.getShapes()[0]
                .lineStyle(this.lineWidth, this.strokeColor, this.strokeAlpha)
                .fillStyle(this.fillColor, this.fillAlpha)
                // top line, right arc
                .startAt(left + radius, top).lineTo(right - radius, top).arc(right - radius, top + radius, radius, 270, 360)
                // right line, bottom arc
                .lineTo(right, boxBottom - radius).arc(right - radius, boxBottom - radius, radius, 0, 90)
                // bottom indent                    
                .lineTo(left + 60, boxBottom).lineTo(left + 50, bottom).lineTo(left + 40, boxBottom)
                // bottom line, left arc
                .lineTo(left + radius, boxBottom).arc(left + radius, boxBottom - radius, radius, 90, 180)
                // left line, top arc
                .lineTo(left, top + radius).arc(left + radius, top + radius, radius, 180, 270)
                .close();

        }
    })
}

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Demo
};

var game = new Phaser.Game(config);
View Code
复制代码

 

6.textBox类型,分页设计对话

 

 参考代码:

复制代码
const COLOR_PRIMARY = 0x4e342e;
const COLOR_LIGHT = 0x7b5e57;
const COLOR_DARK = 0x260e04;

class Demo extends Phaser.Scene {
    constructor() {
        super({
            key: 'examples'
        })
    }

    preload() { 
        this.load.scenePlugin({
            key: 'rexuiplugin',
            url: './rexuiplugin.min.js',
            sceneKey: 'rexUI'
        });
        this.load.image('nextPage', '../../resource/apple_20220918.png');
    }

    create() {
        var lines = [];
        for (var i = 0; i < 3; i++) {
            lines.push(`Page ${i}\n`)
            for (var l = 0; l < 6; l++) {
                lines.push(`- Line ${l}\n`)
            }
            if (i < 2) {
                lines.push('\f\n'); // Page-break
            }
        }
        var content = lines.join('');

        CreateTextBox(this, 400, 100, {
            wrapWidth: 500,
            fixedWidth: 500,
            fixedHeight: 65,
        }).start(content, 50);
    }

    update() { }
}


const GetValue = Phaser.Utils.Objects.GetValue;
var CreateTextBox = function (scene, x, y, config) {
    var wrapWidth = GetValue(config, 'wrapWidth', 0);
    var fixedWidth = GetValue(config, 'fixedWidth', 0);
    var fixedHeight = GetValue(config, 'fixedHeight', 0);
    var textBox = scene.rexUI.add.textBox({
        x: x,
        y: y,

        background: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 20, COLOR_PRIMARY)
            .setStrokeStyle(2, COLOR_LIGHT),

        icon: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 20, COLOR_DARK),

        text: GetBBcodeText(scene, wrapWidth, fixedWidth, fixedHeight),

        action: scene.add.image(0, 0, 'nextPage').setTint(COLOR_LIGHT).setVisible(false),

        space: {
            left: 20, right: 20, top: 20, bottom: 20,
            icon: 10,
            text: 10,
        },

        // page: {
        //     pageBreak: '\f\n'
        // }
    })
        .layout();

    textBox
        .setInteractive()
        .on('pointerdown', function () {
            var icon = this.getElement('action').setVisible(false);
            this.resetChildVisibleState(icon);
            if (this.isTyping) {
                this.stop(true);
            } else if (!this.isLastPage) {
                this.typeNextPage();
            } else {
                // Next actions
            }
        }, textBox)
        .on('pageend', function () {
            if (this.isLastPage) {
                return;
            }

            var icon = this.getElement('action').setVisible(true);
            this.resetChildVisibleState(icon);
            icon.y -= 30;
            var tween = scene.tweens.add({
                targets: icon,
                y: '+=30', // '+=100'
                ease: 'Bounce', // 'Cubic', 'Elastic', 'Bounce', 'Back'
                duration: 500,
                repeat: 0, // -1: infinity
                yoyo: false
            });
        }, textBox)
        .on('complete', function () {
            console.log('all pages typing complete')
        })
    //.on('type', function () {
    //})

    return textBox;
}

var GetBBcodeText = function (scene, wrapWidth, fixedWidth, fixedHeight) {
    return scene.rexUI.add.BBCodeText(0, 0, '', {
        fixedWidth: fixedWidth,
        fixedHeight: fixedHeight,
        fontSize: '20px',
        wrap: {
            mode: 'word',
            width: wrapWidth
        },
        maxLines: 3
    })
}

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Demo
};

var game = new Phaser.Game(config);
View Code
复制代码

 

posted @   Renhr  阅读(165)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示