玩40,000单位军队进行比赛

man*_*sim 7 javascript php canvas

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

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

{ 
'id' => 17854,
'x' => 1488, 
'y' => 1269, 
'team' => 'red', 
'health' => 10,
'target' => [1486, 1271]
}
Run Code Online (Sandbox Code Playgroud)

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

1秒(25帧)= 62.5 mb文件大小.战斗可能持续约30分钟,所以它应该使用112gb.这是不好的.如果我将文件作为二进制数据,它应该减少27倍的位置,或4克,30分钟.那仍然很糟糕.2小时电影需要700mb.

我需要在服务器上保存许多战斗.真正的球员应该组建他们的军队并相互战斗,所以我需要战斗才能得救.每次战斗都是独一无二的,因为每个单位的每次伤害都是随机的0~10,并且在单位杀死一个敌人后,它会再生1个生命值.我正在用PHP进行计算并在服务器上保存文件.所有40.000个单位都在一个屏幕上,所有都可以一次看到,我想要那样.目前,单位是单个2x2像素立方体,红色和蓝色团队,它易于加载javascript.

但是,要用PHP生成文件我需要大约1小时.那是另一个问题.因为对于每一帧,我需要迭代这些40.000个单位(用于更新x/y,搜索附近的敌人或朋友,然后进行伤害并返回目标或杀死敌人坐标),然后再次迭代以取消设置被杀死的单位,然后放入所有这些都进入文件,我需要迭代所有内容并删除用于计算的未使用的数据.要完成这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
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

这是单位:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是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();

});
Run Code Online (Sandbox Code Playgroud)



解决了!在我的工作中向我的同事致敬!他给了我很棒的主意!

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

Yak*_*ont 3

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

'x' => 1488, 
'y' => 1269, 
Run Code Online (Sandbox Code Playgroud)

根据画布大小,可以对其进行压缩。如果画布为 1e6 x 1e6(一百万乘一百万),则有 1e12 个位置,大约可容纳 40 位。

'team' => 'red', 
Run Code Online (Sandbox Code Playgroud)

有 2 条边,这是 1 位。

'health' => 10,
Run Code Online (Sandbox Code Playgroud)

绝大多数单位的生命值都很低。所以我们能做的就是将生命值<15的单位存储在4位中。如果所有位都已设置,我们必须在其他地方查找单位的健康状况(使用 id->health 表)。

'target' => [1486, 1271]
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

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

您想要迭代打包的数据,跳过“死”单元(健康位掩码),将它们解包成可用的结构,评估它们的操作,然后将它们写回。双缓冲是一种选择(从一个缓冲区读取数据,然后写入另一个缓冲区),但它的成本适中。因为单位的大小是固定的,所以你可以快速查找其他单位(并解压它们),如果你的目标是其他单位 ID,或者进行诸如造成伤害之类的事情,这会有所帮助。

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