生命游戏HTML5 Canvas代码

生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。它最初于1970年10月在《科学美国人》杂志中马丁·葛登能(Martin Gardner,1914年11月21日-2010年5月22日。又译:马丁·加德纳)的“数学游戏”专栏出现。

生命游戏其实是一个零玩家游戏,它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个 细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相 反,如果周围活细胞过少,这个细胞会因太孤单而死去。实际中,你可以设定周围活细胞的数目怎样时才适宜该细胞的生存。如果这个数目设定过高,世界中的大部 分细胞会因为找不到太多的活的邻居而死去,直到整个世界都没有生命;如果这个数目设定过低,世界中又会被生命充满而没有什么变化。实际中,这个数目一般选 取2或者3;这样整个生命世界才不至于太过荒凉或拥挤,而是一种动态的平衡。这样的话,游戏的规则就是:当一个方格周围有2或3个活细胞时,方格中的活细 胞在下一个时刻继续存活;即使这个时刻方格中没有活细胞,在下一个时刻也会“诞生”活细胞。在这个游戏中,还可以设定一些更加复杂的规则,例如当前方格的 状况不仅由父一代决定,而且还考虑祖父一代的情况。你还可以作为这个世界的上帝,随意设定某个方格细胞的死活,以观察对世界的影响。
在游戏的进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状已经锁定,不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。

由于游戏是在一个二维平面中进行的,所以可以首先定义一个二元组,方便后续操作:

    //二元组
    function TwoDimensional(x, y){
        var _x = x;
        var _y = y;

        this.GetX = function () { return _x; }
        this.SetX = function (x) { _x = x; }
        this.GetY = function () { return _y; }
        this.SetY = function (y) { _y = y; }

    }

为了美观起见,我们定义圆角矩形来表现我们的细胞:

    //扩展RoundRect类
    //params
    //x : 圆角矩阵左上方x轴坐标
    //y : 圆角矩阵左上方y轴坐标
    //width : 圆角矩阵宽度
    //height : 圆角矩阵高度
    //radius : 圆角矩阵圆角弧度
    //degree : 圆角矩阵线宽
    //color : 笔触颜色
    //fillStyle : 填充颜色
    //isFill : 是否填充
    //isStroke : 是否绘制
    CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius, degree, color, fillStyle, isFill, isStroke) {
        if(typeof isStroke == ‘undefined‘) isStroke = true;
        if(typeof radius == ‘undefined‘) radius = 5;
        this.beginPath();
        this.moveTo(x + radius, y);
        this.lineTo(x + width - radius, y);
        this.quadraticCurveTo(x + width, y, x + width, y + radius);
        this.lineTo(x + width, y + height - radius);
        this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
        this.lineTo(x + radius, y + height);
        this.quadraticCurveTo(x, y + height, x, y + height - radius);
        this.lineTo(x, y + radius);
        this.quadraticCurveTo(x, y, x + radius, y);
        this.lineWidth = degree;
        this.strokeStyle = color;
        this.closePath();
        this.fillStyle = fillStyle;
        if(isStroke) this.stroke();
        if(isFill) this.fill();
    }

然后,定义细胞类

    //细胞类
    //params
    //life 细胞是否生存
    function Cell (life){
        //是否生存
        var _life = life;

        //获得细胞是否生存
        this.IsLive = function (){ return _life; }
        //杀死该细胞
        this.Kill = function (){ _life = false; }
        //复活该细胞
        this.Relive = function (){ _life = true; }
    }

最后,写舞台类

    //舞台类
    //params
    //canvasName : 画布名
    //rows : 行数
    //columns : 列数
    function Stage (canvasName, rows, columns){
        //画布名
        var _canvasName = canvasName;
        //网格行数
        var _rows = rows;
        //网格列数
        var _columns = columns;
        //画布句柄
        var _canvas = document.getElementById(_canvasName);
        //画布高度
        var _canvasHeight = _canvas.height;
        //画布宽度
        var _canvasWidth = _canvas.width;
        //画笔
        var _canvasContext = _canvas.getContext(‘2d‘);
        //格宽
        var _unitWidth = _canvasWidth / _columns;
        //格高
        var _unitHeight = _canvasHeight / _rows;
        //细胞繁衍数
        var _breedNumber = 0;
        //细胞生存数
        var _liveNumber = 0;
        //细胞矩阵
        var _cellMatrx = new Array(_rows);
        for(var i = 0; i < _rows; i++){
            _cellMatrx[i] = new Array(_columns);
            for(var j = 0; j < _columns; j++){
                _cellMatrx[i][j] = new Cell(false);
            }
        }

        //当鼠标移动进时隐藏
        _canvas.style.cursor = ‘none‘;

        //绘制网格
        this.DrawGrid = function () { _drawGrid(); }
        function _drawGrid(){
            _canvasContext.strokeStyle = ‘rgba(0, 51, 51, 0.8)‘;    //网格颜色
            _canvasContext.lineWidth = 1;   //网格宽度
            _canvasContext.beginPath();
            for(var i = 0; i <= _columns; i++){
                _canvasContext.moveTo(i * _unitWidth, 0);
                _canvasContext.lineTo(i * _unitWidth, _canvasHeight);
            }
            for(var i = 0; i <= _rows; i++){
                _canvasContext.moveTo(0, i * _unitHeight);
                _canvasContext.lineTo(_canvasWidth, i * _unitHeight);
            }
            _canvasContext.stroke();
            _canvasContext.closePath();
        }

        //绘制细胞
        this.DrawCells = function () { _drawCells(); }
        function _drawCells(){
            for(var i = 0; i < _rows; i++){
                for(var j = 0; j < _columns; j++){
                    if(_cellMatrx[i][j].IsLive()){
                        _canvasContext.roundRect(j * _unitWidth, i * _unitHeight,
                                _unitWidth, _unitHeight, 10, 2,
                                ‘rgba(102,0,51,1)‘, ‘rgba(102,0,51,1)‘,
                                true, true);
                    }
                }
            }
        }

        //复活某细胞
        this.Relive = function (x, y) { _relive(x, y); }
        function _relive(x, y){ _cellMatrx[x][y].Relive(); }

        //杀死某细胞
        this.Kill = function (x, y) { _kill(x, y); }
        function _kill(x, y) { _cellMatrx[x][y].Kill(); }

        //清空画布
        this.ClearCanvas = function () { _clearCanvas(); }
        function _clearCanvas() { _canvasContext.clearRect(0, 0, _canvasWidth, _canvasHeight); }

        //刷新
        this.Refresh = function () { _refresh(); }
        function _refresh() {
            _canvasContext.clearRect(0, 0, _canvasWidth, _canvasHeight);
            _drawGrid();
            _drawCells();
        }

        //检测细胞是否适合生长
        //params
        //x : x坐标轴
        //y : y坐标轴
        this.Check = function (x, y) { return _Check(x, y); }
        function _Check(x, y) {
            var cellNumber = 0;
            for(var i = -1; i <= 1; i++){
                for(var j = -1; j <= 1; j++){
                    if(i == 0 && j == 0) continue;
                    if(x + i >= 0 && x + i < _rows && y + j >= 0 && y + j < _columns)
                    {
                        if(_cellMatrx[x + i][y + j].IsLive()) { cellNumber++; }
                    }
                }
            }
            return cellNumber;
        }

        //设置细胞繁殖数
        this.SetBreedNumber = function (i) { _breedNumber = i; }

        //设置细胞生存数
        this.SetLiveNumber = function (i) { _liveNumber = i; }

        //检测全网格细胞并刷新网格
        this.CheckAll = function () { _CheckAll(); }
        function _CheckAll() {
            var reliveList = new Array();
            var dieList = new Array();
            for(var i = 0; i < _rows; i++){
                for(var j = 0; j < _columns; j++){
                    var check = _Check(i, j);
                    if(check == _breedNumber) reliveList.push(new TwoDimensional(i, j));
                    else if(check != _liveNumber) dieList.push(new TwoDimensional(i, j));
                }
            }
            for(var i = 0; i < reliveList.length; i++){
                _relive(reliveList[i].GetX(), reliveList[i].GetY());
            }
            for(var i = 0; i < dieList.length; i++){
                _kill(dieList[i].GetX(), dieList[i].GetY());
            }
            _refresh();
        }

        //鼠标移动事件
        _canvas.onmousemove = function (e){
            _refresh();
            _canvasContext.strokeStyle = ‘rgba(255, 100, 100, 0.8)‘;
            _canvasContext.lineWidth = 5;
            _canvasContext.beginPath();
            _canvasContext.moveTo(0, e.pageY - _canvas.offsetTop);
            _canvasContext.lineTo(_canvasWidth, e.pageY - _canvas.offsetTop);
            _canvasContext.moveTo(e.pageX - _canvas.offsetLeft, 0);
            _canvasContext.lineTo(e.pageX - _canvas.offsetLeft, _canvasHeight);
            _canvasContext.stroke();
            _canvasContext.closePath();
        }

        //鼠标移开事件
        _canvas.onmouseleave = function (e) { _refresh(); }

        //鼠标点击事件
        _canvas.onmousedown = function (e){
            var canvasY = e.pageY - _canvas.offsetTop;
            var canvasX = e.pageX - _canvas.offsetLeft;
            var martexY = Math.floor(canvasX / _unitWidth);
            var martexX = Math.floor(canvasY / _unitHeight);
            if(_cellMatrx[martexX][martexY].IsLive()){
                _cellMatrx[martexX][martexY].Kill();
            }
            else{
                _cellMatrx[martexX][martexY].Relive();
            }
            _refresh();
        }

    }

为了方便控制,在body中如下编写:

<body>
    <p>细胞繁衍数   :<input id="CellsReproduceNumber" type="number" maxlength="1" value="0" /></p>
    <p>细胞生存数   :<input id="CellsLiveNumber" type="number" maxlength="1" value="0" /></p>
    <p>繁衍周期(毫秒):<input id="CellsBreedCycle" type="number" max="6" value="500" /></p>
    <p><input id="StartOrStopButton" type="button" value="开始" onclick="OnClickButton()"/></p>
    <canvas id="Stage" width="720" height="720"></canvas>
    <script>
        var isStart = false;
        var intervalHandler;
        var stageObject = new Stage("Stage", 50, 50);
        stageObject.Refresh();

        function OnClickButton(){
            if(isStart){
                isStart = false;
                clearInterval(intervalHandler);
                document.getElementById(‘StartOrStopButton‘).value = ‘开始‘;
            }
            else{
                stageObject.SetBreedNumber(document.getElementById(‘CellsReproduceNumber‘).value);
                stageObject.SetLiveNumber(document.getElementById(‘CellsLiveNumber‘).value);
                intervalHandler = setInterval(‘stageObject.CheckAll()‘, document.getElementById(‘CellsBreedCycle‘).value);
                document.getElementById(‘StartOrStopButton‘).value = ‘停止‘;
                isStart = true;
            }
        }
    </script>
</body>

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。