javascript - 玩 40.000 单位军队进行游戏

标签 javascript php canvas

关闭。这个问题需要更多focused .它目前不接受答案。












想改善这个问题吗?更新问题,使其仅关注一个问题 editing this post .

6年前关闭。




Improve this question




解决了!! (看我上次编辑)

我想在 Canvas 上进行 20.000 对 20.000 单位的军队战斗。因此,对于每个单位数据是:

{ 
'id' => 17854,
'x' => 1488, 
'y' => 1269, 
'team' => 'red', 
'health' => 10,
'target' => [1486, 1271]
}

我会实时看到这场战斗(每秒 25 帧)。
如果我用 Json 生成 1 帧并保存在文件中,它是 2.5mb 大小(40k 这样的单位与此数据)。

1 秒(25 帧)= 62.5 mb 文件大小。并且战斗可能会持续大约 30 分钟,所以它应该使用 112GB。这不好。如果我将文件作为二进制数据,它应该减少 27 倍的空间,或者 4GB 30 分钟。那还是不好。 2小时的电影需要700mb。
我需要在服务器上保存很多战斗。真正的玩家应该组建他们的军队并互相战斗,所以我需要拯救战斗。每场战斗都是独一无二的,因为每个单位的每一次伤害都是随机的 0~10,并且在单位杀死一个敌人后,它会恢复 1 点生命值。我正在用 PHP 进行计算并将文件保存在服务器上。所有 40.000 个单位都在一个屏幕上,所有单位都可以同时显示,我想要这样。目前,单位是单个 2x2 像素立方体,红色和蓝色团队,它很容易加载 javascript。
但是,要使用 PHP 生成文件,我需要大约 1 小时。那是另一个问题。因为对于每一帧我都需要迭代这 40.000 个单位(用于更新 x/y,搜索附近的敌人或 friend ,然后造成伤害并返回目标或杀死的敌人坐标),然后再次迭代以取消设置被杀死的单位,然后在放置之前所有这些都放入文件中,我需要遍历所有内容并删除用于计算的未使用数据。为了完成这 30 分钟的战斗,我需要重复 45000 次。此外,每分钟都有越来越少的单位。但我的观点是以某种方式在不到一分钟的时间内生成所有文件,只需要一种合乎逻辑的方式,如果存在的话。
问题:
1) 将文件保存在服务器上并使文件大小更小的最佳方法是什么?
(到目前为止是使用二进制数据并压缩为zip)
2)计算我的战斗的最快方法是什么?
(目前是用C++编译)

//已编辑。这是我的整个游戏代码,就像这样:)

这是主要操作:
class Simulator
{
    private $units;
    private $places = [];
    private $oldPlaces = [];

    public function initiateGame() {
        $this->createUnits();
        $this->startMoving();
    }

    private function createUnits() {
        foreach(range(0, 150) as $column) { // i like exact army formation to look nice, so its 150x140=21000 units
            foreach (range(0, 140) as $row) {
                $this->setUnits($column, $row);
            }
        }
        $this->oldPlaces = $this->places;
    }

    private function setUnits($column, $row) {
        $beginning_of_team_A = 6; //starting point on canvas for A unit to look nice on screen
        $unit_size_and_free_place = 6; //unit size= 3x3 (look js), and free place between each unit is 3 pixels.
        $beginning_of_team_B = 1100; // next side where enemy army starts appearing
        $x_a = $beginning_of_team_A + $column * $unit_size_and_free_place; // team A
        $y = $beginning_of_team_A + $row * $unit_size_and_free_place; // same for both teams
        $unitA = new Unit($x_a, $y, 1); // 1 is team A (it goes always +1 pixel every frame)
        $this->units[] = $unitA;
        $x_b = $beginning_of_team_B + $column * $unit_size_and_free_place;  // team B
        $unitB = new Unit($x_b, $y, -1); // -1 is team B (it goes always -1 pixel every frame)
        $this->units[] = $unitB;
        $this->places[$x_a.','.$y] = 1; // now that way tracking units, and calculating their next move
        $this->places[$x_b.','.$y] = -2;
    }

    private function startMoving() {
        set_time_limit(30000); // by default after 1 minute it throws exception
        foreach(range(0, 400) as $frame) { //giving 400 frames is like 400/40=10 seconds of action
            $this->places = [];
            foreach($this->units as $unit) {
                $returned = $unit->move($this->oldPlaces); //giving whole units list to every unit to look forward
                $this->places[$returned[0]] = $returned[1]; // returns (next x/y position as string ['1514,148'] ) = ( id as int [15] )
            }
            file_put_contents('assets/games/'.$frame.'.json', json_encode($this->units)); // writing into file  every frame and it uses ~2mb
            $this->oldPlaces = $this->places; //resetting old positions
        }
    }

}

这是单位:
class Unit
{
    public $x = 0;
    public $y = 0;
    public $team = 1;
    public $stopped;

    public function __construct($x, $y, $team, $stopped = false) {
        $this->x = $x;
        $this->y = $y;
        $this->team = $team;
        $this->stopped = $stopped;
    }

    public function move($places) {
        $this->checkForward($places);
        return [$this->x.','.$this->y, $this->team];
    }

    private function checkForward($places) {
        $forward = $this->x + $this->team; // TODO: find out formula to replace the 4 ifs
        $forward1 = $this->x + $this->team*2;
        $forward2 = $this->x + $this->team*3;
        $forward3 = $this->x + $this->team*4;
        if(isset($places[$forward.','.$this->y])) {
            $this->stopped = true;
        } else if (isset($places[$forward1.','.$this->y])) {
            $this->stopped = true;
        } else if (isset($places[$forward2.','.$this->y])) {
            $this->stopped = true;
        } else if (isset($places[$forward3.','.$this->y])) {
            $this->stopped = true;
        } else {
            $this->stopped = false;
        }

        if($this->stopped == false) { // move forward it is not stopped
            $this->x = $this->x + $this->team;
        }
    }
}

这是js:
var app = angular.module('app', []);

app.controller('game', function($scope, $http, $interval) {
    var canvas  = document.getElementById("game"),
        context = canvas.getContext("2d");
    var frame = -2;
    $scope.attacking = false;
    var units = [];

    function start_animation_loop() {
        $scope.promise = $interval(function() {
            if($scope.attacking == true) {
                frame ++;
                if(frame >= 0) {
                    downloadFile();
                    animate();
                }
            }
        }, 40 );
    }

    function downloadFile() {
        $http.get('assets/games/'+frame+'.json').success(function(response) {
            units = response;
        });
    }

    function animate() {
        clear_canvas();
        draw();
    }

    function clear_canvas() {
        context.clearRect(0, 0, 1800, 912);
    }

    function draw() {
        for(var a=0; a<units.length; a++) {
            context.beginPath();
            context.fillRect(units[a]['x'], units[a]['y'], 3, 3);
            if(units[a]['team'] == 1) {
                context.fillStyle = 'red';
            } else {
                context.fillStyle = 'blue';
            }
        }
    }
    start_animation_loop();

});

解决了! 感谢我工作中的同事!他给了我绝妙的主意!

为了得到我需要的结果,我只需要在下一场战斗中生成随机数(0~10000)并将其放入单个 MySQL 数据库中。此外还放有阵型,单位,他们的起始力量,健康和其他一切。
所有计算都使用 javascript:
使用一个常数(从后端给出),我制定了一个公式来始终重现相同的军队战斗 -> 每个单位都会向前移动,并且无论如何它们总是在同一时间停止,无论计算如何。唯一性是每个单位造成的随机伤害以及之后的处理过程。并且所有的伤害都只是他们的“x/y 位置与常数相比”,并且做任何事情来获得单个伤害,每个单位都是随机的,因为它们都在不同的 map 位置,但伤害总是 0~10。使用相同的常数,所有单位在计算后将始终造成相同的伤害,并且在每次重播时总是移动相同,在每次重播时死亡和造成相同的伤害。所有最困难的工作都将在 javascript 上进行 - 使用这个常数进行计算。
我的随机数可以是任意的。如果第一场比赛我产生随机数“17”,下一场比赛我产生随机数“19666516546”,这并不意味着数字“17”的战斗会造成更少的伤害——他们都会造成0~15的“随机”伤害到每个单位,但以相同的编队、单位编号、起始位置和这个随机生成的数字重播将始终相同 -> 不再需要保存任何文件!而且我可以添加各种规范效果,添加诸如防御、回避之类的东西,并且所有这些都可以放入两个 MySQL 行中 - 对于每个团队:) 酷!

最佳答案

id可以隐含在存储介质中。当然,这意味着您必须节省间隙,但您可以压缩所述间隙。

'x' => 1488, 
'y' => 1269, 

根据 Canvas 大小,可以压缩。如果 Canvas 是 1e6 x 1e6(一百万乘一百万),则有 1e12 个位置,可容纳约 40 位。
'team' => 'red', 

有 2 个边,这是 1 位。
'health' => 10,

绝大多数单位的生命值都很低。所以我们可以做的是将健康值 < 15 的单位存储在 4 位中。如果所有位都设置了,我们必须在别处查找单位健康状况(使用 id->health 表)。
'target' => [1486, 1271]

我们可以为每个单元存储一个独立的目标,但这可能与 UI 的工作方式不匹配。你可能会选择一堆单位,然后告诉他们去某个地方,不是吗?一个位置约 40 位,引用计数约 24 位,每个目标 8 个字节。

如果我们给每一方限制约 65k 个目标,即 16 位。

16+4+1+40 = 61 位。这意味着我们还有 3 个位可以用来将它们打包成每个单元的 64 位。

每单元 64 位,即每边 160k。加上多达半兆的目标数据,但可以动态处理。

加上一个健康溢出表(将 id 映射到健康),它通常应该接近空。如果您有时这样做,您可以设置前后 id 映射以保持一致的历史记录(例如,当一半单位死亡时,您使用前后 id 映射进行压缩传递)。

如果 id 不需要一致,您可以将单位压缩,使它们不再稀疏。

目标技巧——如果小于 40,000,目标可能是一个单位 ID,如果高于该值,它将是一个航路点。这将您的航路点减少到大约 15k 个。 (我假设目标是由用户界面设置的,有人会选择一大块单位,命令他们去某个地方)。

您可能想要遍历打包的数据,跳过“死”单元(健康的位掩码),将它们解包成可用的结构,评估它们的 Action ,然后将它们写回。双缓冲是一种选择(从一个缓冲区读取,然后写入另一个缓冲区),但它的成本适中。因为单位是固定大小,所以您可以快速查找其他单位(并解压缩它们),这在您的目标是其他单位 ID 以及造成伤害等情况时会有所帮助。

单缓冲使事情变得更容易,因为诸如同时损坏之类的事情很棘手。这确实意味着低 id 单位首先行动——你可以通过每回合抛硬币来解决这个问题,以确定你是向前还是向后迭代(并将一侧放在低 id 中,另一侧放在高 id 中),或者让在战斗开始时进行主动检查。

关于javascript - 玩 40.000 单位军队进行游戏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31277102/

相关文章:

php - 通过 slug 或 title wordpress 获取页面内容

php - 将字符串分解为嵌套数组

javascript - 使用 html5 canvas 和纯 javascript 绘制散点图 没有带对数轴的库

javascript - 等效于 SVG 中的 Canvas quadraticCurveTo

javascript - 如果列表项的内容为零,则添加消息

JavaScript 在图像上链接元素

javascript - Vue Full Calendar (next, prev) 按钮触发

php - 你会如何处理这个 php/mysql

javascript - 本地图缩放时,圆圈在移动设备上消失

javascript - 对于快速 Web 动画,Javascript/jQuery 或 CSS3?