0

我目前的项目是一个简单的多人游戏。客户端和服务器都是用 Typescript 编写的。客户端只处理输入并渲染游戏,所有逻辑都在服务器端实现。

服务器代码使用 nodejs 执行,结构如下

  • main.ts 包含一个快速服务器并使用客户端脚本提供 html 文件。此外,它设置了 socket.io,创建了一个新的 Game 实例,并Player为每个连接的套接字创建了一个新的实例。

  • game.ts 导出类GamePlayer. Game实际上只是所有重要数据的容器。它存储所有玩家的列表和所有游戏对象的列表。Game实现requestSpawn(...)检查是否可以生成新游戏对象的方法。该类Player只是 socket.io 套接字的包装器。它处理传入和传出的消息。如果客户端尝试生成 GameObject,则会将消息发送到服务器并到达存储在Player实例中的套接字。然后Player实例调用requestSpawn以尝试生成所需的GameObject.

  • GameObjects.ts 导出接口GameObject和该接口的各种实现。

游戏运行如下:

  1. 一个人在 html-canvas 内点击
  2. 一个新的“REQUESTSPAWN”消息被打包并发送到服务器
  3. Player实例接收消息并调用其requestSpawn实例Game
  4. Game实例在正确的位置创建一个GameObject正确类型的 new 并将其添加到GameObjects
  5. GameObject现在应该更新这个新的。

它不是。以下是相关代码:

游戏.ts

import go = module("GameObjects"); 
import util = module("Utilities");

//...

export class Game {
    private players: Player[];
    public gameObjects:go.GameObject[];
    private gameObjectCounter: number;

    constructor() {
        this.players = [];
        this.gameObjectCounter = 0;
        this.gameObjects = [];
        var prev = Date.now();
        var deltaTime = Date.now() - prev;
        setInterval(() =>{ deltaTime = Date.now() - prev; prev = Date.now(); this.update(deltaTime); }, 200);
    }

    broadcast(msg: Message) {
        this.players.forEach((player) => { player.send(msg); });
    }

    requestSpawn(msg:RequestSpawnMessage, clientID:number): bool {
        var pos = new util.Vector2(msg.x, msg.y);        
        var o: go.GameObject;
        switch (msg.tag) {
            case UID.FACTORY:                                        
                o = new go.Factory(pos.clone(), this.players[clientID], this.newGameObject());
            case UID.ROBOT:
                o = new go.Robot(pos.clone(), this.players[clientID], this.newGameObject());
        }
        this.broadcast(new SpawnMessage(msg.tag, o.id, clientID, pos.x, pos.y));
        this.gameObjects.push(o);
        console.log(this.gameObjects);
        o.update(1);
        console.log("tried to update the factory");
        return true;
    }

    update(deltaTime){
        this.gameObjects.forEach((object) =>{object.update(deltaTime); });
    }

    addPlayer(socket: Socket) {        
        var player = new Player(this, socket, this.players.length);
        this.players.push(player);
    }
    newGameObject() : number {
        return this.gameObjectCounter++;
    }
}

游戏对象.ts

export import util = module("Utilities");
export import s = module("server");
export import g = module("game");

export interface GameObject{
    tag: g.UID;
    id:number;
    player: g.Player;
    clientId: number;
    pos:util.Vector2;
    getPos():util.Vector2;
    setPos(newPos:util.Vector2);
    // !TODO how to make that const?
    boundingBox: util.Rectangle;
    update(deltaTime:number);
}

export class Factory implements GameObject {
    tag: g.UID;
    id: number;
    player: g.Player;
    clientId: number;
    server: s.Server;
    //variables for handling the delay between spawning robots
    private current_time: number;
    public delay: number;
    boundingBox: util.Rectangle;

    public static dimensions = new util.Vector2(30, 30);
    constructor(pos: util.Vector2, player:g.Player, id: number) {
        this.pos = pos;
        this.tag = g.UID.FACTORY;
        this.player = player;
        this.clientId = this.player.getID();
        this.current_time = 0;
        this.delay = 1;
        this.id = id;
        this.boundingBox = new util.Rectangle(pos, Factory.dimensions.x, Factory.dimensions.y);
        console.log("just created a factory");
        //this.update(1);
    }

    pos: util.Vector2;
    getPos() { return this.pos; }
    setPos(pos: util.Vector2) { this.pos = pos; }

    public once = true;
    //check if it's time to create a new robot
    public update(deltaTime: number) {
        console.log("updating a factory");

        //this code will produce a robot just once, this is handy for development, since there's not so much waiting time
        if (this.once) { this.player.requestSpawn(g.UID.ROBOT, this.pos.x, this.pos.y); console.log("just spawned a robot"); }
        this.once = false;
        /*this.current_time += deltaTime/1000;
        if (this.current_time > this.delay*(Factory.count+1)/(Mine.count+1)) {
            this.current_time = 0;
            this.spawnRobot();
        }*/
    }
}


//this will be the fighting robot meant to destroy enemy factories
export class Robot implements GameObject{
    tag: g.UID;
    id: number;
    player:g.Player;
    clientId: number;
    game: g.Game;
    boundingBox: util.Rectangle;
    // ! TODO constants should have capital letters.
    public static radius = 15;
    constructor(pos:util.Vector2,player:g.Player,id:number){ 
        this.tag = g.UID.ROBOT;
        this.player=player;
        this.clientId = this.player.getID();
        this.boundingBox = new util.Rectangle(pos, Robot.radius, Robot.radius);
    }

    pos:util.Vector2;
    getPos(){return this.pos;}
    setPos(pos:util.Vector2){this.pos=pos;}

    //now the robot is moved by keyboard input but soon it will check the gameObjects array and search for the closest enemy,
    //in order to attack it
    public update(deltaTime: number) {

    }
}

现在第一次调用工厂实例的更新方法会产生一个机器人,然后工厂“休眠”。机器人在他们的更新方法中什么都不做。

我想引导您关注两行代码: - 在requestSpawn方法中,GameObject立即使用 deltaTime=1 更新

这意味着在生成工厂之后,也应该直接生成机器人。但这不会发生。我console.log在方法中添加了一个调用requestSpawn。它成功打印了“刚刚尝试更新工厂”,但没有任何反应。所以我认为更新方法无法正常工作并在console.log那里添加了一个调用。这是方法的第一行,Factory's update应该打印“更新工厂”。但这永远不会发生。

我真的很困惑。该方法应该被调用,但它不是。虽然它被宣布为公开,但我认为这个问题可能与访问权限有关。所以这是第二行代码,我想指出: - 在工厂的构造函数中,我已经注释掉了对this.update(1).

我认为至少自己的构造函数应该能够调用更新方法。它的确是。当这一行没有被注释掉时,更新被调用一次。然后工厂尝试生成一个新机器人并调用requestSpawn()其实例Game。结果,一个新的机器人被创建SpawnMessage出来,并提供给所有的客户。机器人甚至出现在客户端的浏览器选项卡中。

因此很明显该方法没有被调用。一切正常,消息解析、工厂更新和机器人创建都是正确的。唯一的问题是所有从内部更新的调用Game都没有执行。什么地方出了错 ?

4

1 回答 1

2

您发布了很多代码,可能还有其他问题,但至少有一个问题是您缺少breakswitch 语句中的...

switch (msg.tag) {
        case UID.FACTORY:                                        
            o = new go.Factory(pos.clone(), this.players[clientID], this.newGameObject());
        case UID.ROBOT:
            o = new go.Robot(pos.clone(), this.players[clientID], this.newGameObject());
}

...因此总是创建一个Robot,它在其update()方法中没有任何痕迹。(如果您尝试创建 a ,您会成功,但随后会立即通过将 new 分配给相同的 var 来Factory覆盖它)。Roboto

考虑这个简化的例子:

interface GameObject {
    update():void;
}

class Factory implements GameObject {
    update():void {
        console.log("Factory");
    }
}

class Robot implements GameObject {
    update():void {
        console.log("Robot");
    }
}

class Test {

    private o:GameObject;

    constructor(index:number){
        switch(index){
            case 1:
                this.o = new Factory();
            case 2:
                this.o = new Robot();
        }
        this.o.update();
    }
}

class BreakTest {

    private o:GameObject;

    constructor(index:number){
        switch(index){
            case 1:
                this.o = new Factory();
                break;
            case 2:
                this.o = new Robot();
                break; // Not necessary in final case, but good form IMO.
        }
        this.o.update();
    }
}

var t = new Test(1);       // Traces 'Robot'
var tt = new Test(2);      // Traces 'Robot'

var b = new BreakTest(1);  // Traces 'Factory'
var bt = new BreakTest(2); // Traces 'Robot'

参考

于 2013-03-10T10:16:30.047 回答