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 @ 2022-11-26 11:36  Renhr  阅读(202)  评论(0)    收藏  举报