Screeps 游戏指南

Screeps 控制台结合可视化——优化手操体验

2022-03-23  本文已影响0人  masterkeze

前言

在游玩Screeps中,不可避免的会需要手动调节参数、发起指令,传统的手操一般有以下方式

  1. 在控制台中直接修改 Memory: Memory.xxx.xxx = xxx
  2. 在控制台调用global上提前挂载好的方法: BuyOrder("orderid",10000)
  3. 插旗子、移动旗子
  4. 手动放置construction sites,摧毁建筑等

从笔者个人的实践出发,第一、二种方式的体验是比较糟糕的,在控制台敲代码既没有补全,也没有提示,参数也比较多。而且随着业务量的增大,挂载的方法也越来越多,如何统一管理这些方法,避免冲突也越来越重要。

系统介绍

因此,在“懒”的驱动下,笔者设计并实现了一套控制台系统,统一管理全局挂载的方法。这套系统有以下特点

  1. 节点和指令以树状形式构成了整个系统,节点包含了若干个子节点、指令
  2. 键入节点的名称或缩写,可以进入节点的语境,同时挂载其子节点和指令
  3. 键入指令的名称或缩写,以及参数(如有),可以发起指令
  4. 节点可以是静态的(写死),也可以是动态的(进入语境时生成)
  5. 每个节点,都有唯一的“路径”,路径本身就包含了一些信息,这些信息可以作为默认的参数,在调用指令时,不需要再传入这些信息。
  6. 节点和节点之间相互隔离,即使指令重名了,但因为语境不同,也不会产生冲突。
  7. 支持添加钩子函数,在节点挂载,退出时调用

效果展示

基础指令
效果展示

在候选菜单中,子节点,都是以"/"结尾的,键入子节点的名称或缩写,会进入子节点

值得注意的是每次键入节点的名称,都会自动触发 list 指令,展示当前节点下所有可用的子节点、指令。图中的"myroom"下的子节点,以及他的缩写,都是动态生成出来的。而且因为都配置了缩写,敲1-2个字母,就能在菜单间快速切换了。

数据结构

// 指令
interface Command {
    // 名称
    name: string;
    // 缩写
    alias?: string;
    // 描述
    description: string;
    // 参数
    parameters: string[];
    // 回调函数
    callback(...args: any[]): string;
}
// 节点
interface Dir {
    name: string;
    alias?: string;
    // 离开节点时的钩子函数
    onLeave?(): string;
    description: string;
    // 子节点
    dirs: Dir[];
    // 指令
    cmds: Command[];
}

如何实现动态的节点和指令

使用 getter 和 setter 在访问时计算出节点

动态节点的一个例子

export const myroom: Dir = {
    name: "myroom",
    alias: "mr",
    description: "我的房间",
    cmds: [],
    get dirs() {
        const roomNames = getMyRoomNames();
        let i = 0;
        let dirs: Dir[] = [];
        _.forEach(roomNames, roomName => {
            let dir: Dir = {
                name: roomName,
                alias: `r${i}`,
                description: "管理我的房间",
                cmds: [],
                get dirs() {
                    // 子节点的子节点也是动态的
                    return myRoomDirs(roomName);
                }
            };
            dirs.push(dir);
            i += 1;
        });
        return dirs;
    }
};

控制多个房间时,缩写会按照 r0,r1,r2 .... 的顺序自动生成

动态指令的一个例子

function level(roomName: string): Dir {
    return {
        name: "level",
        alias: "l",
        description: "每级建筑规划",
        onLeave: () => {
            Memory._lpRoomName = "";
            return `关闭${roomName}建筑级别可视化`;
        },
        dirs: [],
        get cmds() {
            Memory._lpRoomName = roomName;
            let cmds: Command[] = [];
            for (let i = 1; i <= 8; i++) {
                let cmd: Command = {
                    name: `level${i}`,
                    alias: `l${i}`,
                    description: `等级${i}的建筑规划`,
                    parameters: [],
                    callback: () => {
                        global._lpStructures = getLevelPlan(roomName, i);
                        return `查看等级${i}的建筑规划`;
                    }
                };
                cmds.push(cmd);
            }
            return cmds;
        }
    };
}

同样用到了 getter setter

如何挂载节点和指令

使用 Object.defineProperty,记得设置 configurable为true

    Object.defineProperty(global, "help", {
        // 这个不加的话,就不能修改了
        configurable: true,
        get: () => {
            let output = "";
            output += "home\t返回主页\n";
            output += "dir\t当前路径\n";
            output += "back\t返回上一级\n";
            output += "list\t可用路径/命令\n";
            output += "help\t帮助";
            return output;
        }
    });

进入某个节点的语境时,需要挂载他所有的子节点、指令,还要记得挂载它们的缩写。对于有参数的指令,需要挂载到 value上,而不是使用 getter。
为了记录路径,需要维护一个 list,存放当前进入过的节点,每进入一个子节点,就push一次,每退出一个节点,就pop一次,然后再挂载最右侧节点的子节点及指令。如果list为空,就挂载home节点。

一个提升体验的优化

因为节点的信息是对象,所以存放在global下,每次global reset的时候,都会丢失这部分信息,这在sim环境下是很不方便的,因此我还序列化存储了路径:home/myroom/sim/constructionPlan/design,在global reset后,自动按次序键入这些节点,以此来保证sim环境下的流畅使用。

    if (Memory._currentDirs) {
        const paths = Memory._currentDirs.split("/");
        for (let i = 0; i < paths.length; i++) {
            const path = paths[i];
            if (!(global as any)[path]) {
                break;
            }
        }
    }

结合可视化的使用案例

Screeps通过 RoomVisual 支持房间中的可视化。在实际使用中,除开那些一直开启的报表类的可视化,一些可选的可视化功能,经常需要手操启用或关闭,笔者的控制台系统非常适合对接这些可选的可视化功能。下面展示一个使用案例,或许会给你带来一些灵感。


show2.gif
上一篇下一篇

猜你喜欢

热点阅读