草庐IT

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

coder 2024-01-03 原文

关闭。这个问题需要更多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/

有关javascript - 玩 40.000 单位军队进行游戏的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  5. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  6. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  7. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  8. ruby - 我需要从 facebook 游戏中抓取数据——使用 ruby - 2

    修改(澄清问题)我已经花了几天时间试图弄清楚如何从Facebook游戏中抓取特定信息;但是,我遇到了一堵又一堵砖墙。据我所知,主要问题如下。我可以使用Chrome的检查元素工具手动查找我需要的html-它似乎位于iframe中。但是,当我尝试抓取该iframe时,它​​是空的(属性除外):如果我使用浏览器的“查看页面源代码”工具,这与我看到的输出相同。我不明白为什么我看不到iframe中的数据。答案不是它是由AJAX之后添加的。(我知道这既是因为“查看页面源代码”可以读取Ajax添加的数据,也是因为我有b/c我一直等到我可以看到数据页面之后才抓取它,但它仍然不存在)。发生这种情况是因为

  9. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  10. ruby - 捕获 Ruby Logger 输出以进行测试 - 2

    我有一个像这样的ruby​​类:require'logger'classTdefdo_somethinglog=Logger.new(STDERR)log.info("Hereisaninfomessage")endend测试脚本行如下:#!/usr/bin/envrubygem"minitest"require'minitest/autorun'require_relative't'classTestMailProcessorClasses当我运行这个测试时,out和err都是空字符串。我看到消息打印在stderr上(在终端上)。有没有办法让Logger和capture_io一起玩得

随机推荐