Функция Draw Mixing Properties - PullRequest
       0

Функция Draw Mixing Properties

0 голосов
/ 01 января 2019

Я пытаюсь создать 2d платформер, и код является его основой.

По неизвестной причине моя последняя функция draw смешивает свойства других функций (особенно цвет, ширину линии и т. Д.).

Если есть причина типа ("this."неуместное функционирование и т. д.)

Я хочу знать о дальнейших проектах.

Любой хороший ответ будет полностью оценен!.

/* main.js */

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d")

function Shooter() {
    this.x = 100;
    this.y = 500;
    this.size = 50;
    this.color = "blue";
    this.borderColor = "black";
    this.borderWidth = 5;
    this.draw = function() {
        ctx.fillRect(this.x, this.y, this.size, this.size);
        ctx.strokeRect(this.x, this.y, this.size, this.size);
        ctx.fillStyle = this.color;
        ctx.strokeStyle = this.borderColor;
        ctx.lineWidth = this.borderWidth;
    }
}

function Gun() {
    this.x = sh.x + sh.size / 2 + 10;
    this.y = sh.y + sh.size / 2;
    this.color = "grey";
    this.borderColor = "brown";
    this.borderWidth = 1;
    this.width = 20;
    this.height = 10;
    this.draw = function() {
        ctx.fillRect(this.x,this.y,this.width,this.height);
        ctx.strokeRect(this.x,this.y,this.width,this.height);
        ctx.fillStyle = this.color;
        ctx.strokeStyle = this.borderColor;
        ctx.lineWidth = this.borderWidth;
    }
}

function Bullet() {
    this.x = sh.x + sh.size * 2;
    this.y = sh.y + sh.size / 2;
    this.color = "orange";
    this.radius = 5;
    this.vx = 20;
    this.borderColor = "green";
    this.borderWidth = 2;
    this.draw = function() {
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        ctx.fillStyle = this.color;
        ctx.strokeStyle = this.borderColor;
        ctx.lineWidth = this.borderWidth;
        ctx.stroke();
    }
}
var sh = new Shooter();
var g = new Gun();
var b = new Bullet();

function draw() {
  
    sh.draw();

    g.draw();
    
    b.draw();
    
requestAnimationFrame(draw);
}

draw();
/* main.css */

html, body {
    overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="main.css" />
   
</head>
<body>
    <canvas id="canvas" width="1536px" height="754px"></canvas>
    <!-- device innerWidth and innerHeight -->
<!-- make fullScreen to see the issue -->       
    
    <script src="main.js"></script>
</body>
</html>

Ответы [ 2 ]

0 голосов
/ 01 января 2019

Некоторые дополнительные очки.

Существующий ответ решит вашу проблему, однако у меня есть время и я заметил некоторые моменты в вашем коде, которые можно улучшить.

Производительность - король

При написании игр (или, если уж на то пошло, анимации контента) вы попадете в точку, где сложность анимации (количество анимированных и нарисованных элементов) достигает стадии, когда устройство больше не можетсделать это на полной частоте кадров.Это становится большей проблемой, когда вы пытаетесь охватить больший диапазон устройств.

Чтобы получить максимальную скорость на строку кода в Javascript, вы должны знать о некоторых простых правилах в отношении объектов и того, как онисоздаются и уничтожаются (освобождают память для других объектов).

Управляется JavaScript

Это означает, что программисту не нужно беспокоиться о памяти.Вы создаете объект, и память для него найдена для вас.Когда вам больше не нужен объект, javascript очистит память, чтобы освободить его для других объектов.

Это облегчает программирование в Javascript.Однако в анимации это может стать проблемой, поскольку код, который управляет выделением и очисткой памяти (AKA удаляет выделенную память или сборщик мусора GC), требует времени.Если вашей анимации требуется много времени для вычисления и рендеринга каждого кадра, тогда GC вынужден блокировать ваш сценарий и очистку.

Такое управление памятью является крупнейшим источником Jank в анимациях (играх) Javascript.

Также вводится дополнительная обработка для создания объектов, так как при создании нового объекта должна быть расположена и выделена свободная память.Это ухудшается на устройствах низкого уровня с небольшим количеством RAM.Создание новых объектов чаще заставит GC освободить память для нового объекта (украсть драгоценные циклы ЦП).Это делает производительность на младших устройствах не линейным, а скорее логарифмическим.

Типы объектов.

Объекты (игрок, пикапы, патроны, FX) можно классифицировать по тому, как долго онижить и сколько может существовать за один раз.Время жизни объекта означает, что вы можете воспользоваться преимуществами того, как JS управляет памятью, для оптимизации объектов и использования памяти.


Один экземпляр объекта.

Это объекты, которые существуют только один раз ванимация.То есть иметь время жизни от начала до конца (в игре, которая может быть от начала уровня до конца уровня).

Например, игрок, отображение счета.

Пример

Наилучший способ создания этих объектов - это создание одноэлементной или объектной фабрики.В приведенном ниже примере с маркерами для создания игрока используется одноэлементная

фабрика объектов EG

function Shooter() {
    // Use closure to define the properties of the object
    var x = 100;
    var y = 500;
    const size = 50;
    const style = {
        fillStyle : "blue",
        strokeStyle : "black",
        lineWidth : 5,
    }
    // the interface object defines functions and properties that
    // need to be accessed from outside this function
    const API = {
        draw() {
            ctx.fillStyle = style.fillStyle;
            ctx.strokeStyle = style.strokeStyle;
            ctx.lineWidth = style.lineWidth;

            ctx.fillRect(x, y, size, size);
            ctx.strokeRect(x, y, size, size);

            // it is quicker to do the above two lines as
            /*
            ctx.beginPath(); // this function is done automatically 
                             // for fillRect and strokeRect. It                                 
            ctx.rect(x, y, size, size);
            ctx.fill();
            ctx.stroke();
            */
        }
    }
    return API;
}

Вы используете его, как и любой другой объект

const player = new Shooter();
// or
const player = Shooter();  / You dont need the new for this type of object

// to draw

player.draw();

Много экземпляров объекта.

Это объекты с очень коротким сроком службы, возможно, несколько кадров.Они также могут существовать сотнями (например, искры FX во взрыве или быстрые огненные пули)

В вашем коде у вас есть только одна пуля, но я могу представить, что у вас может быть много, или, скорее, чемпули, это могут быть gribble или искры FX.

Instantiation

Создание объектов требует циклов ЦП.Современный JS имеет много оптимизаций и, следовательно, нет большой разницы в том, как вы создаете объекты.Но есть разница, и использование лучшего метода окупается, особенно если вы делаете это 1000 раз в секунду.(последняя написанная мной игра JS обрабатывает до 80 000 объектов FX в секунду, большинство живут не более 3-6 кадров)

Для многих недолговечных объектов задайте прототип или используйте синтаксис класса (это сокращает время создания наоколо 50%).Храните элементы в пуле, когда они не используются, чтобы остановить попадания в GC и уменьшить накладные расходы на создание экземпляров.Будьте умны при рендеринге и не тратьте впустую время, ожидая, пока графический процессор сделает бессмысленные изменения состояния.

Память

Эти недолговечные объекты являются основным источником замедления, и JANK из-за накладных расходов на управление памятью создает и удаляет их.

Для борьбы с накладными расходами на управление памятью вы можете сделать это самостоятельно.Лучший способ сложен (используйте предварительно выделенный (при начале уровня) пузырьковый массив и задайте максимальное число для каждого объекта на уровне).

Пул объектов

Самое простое решение, которое обеспечит вам 90% лучших и дает неограниченные возможности (зависит от общего объема ОЗУ), - это использование пулов объектов.

Пул - это массив неиспользуемых объектов, которые вы обычно позволяете GCудалять.Активные объекты хранятся в массиве.Они делают свое дело, и когда они сделаны, они перемещаются из массива объектов в пул.

Когда вам нужен новый объект, а не создаете его с помощью new Bullet(), вы сначала проверяете, есть ли у пула такой объект.Если это так, вы берете старый объект из пула, сбрасываете его свойства и помещаете его в активный массив.Если пул пуст, вы создаете новый объект.

Это означает, что вы никогда не удаляете объект (за время существования уровня / анимации).Поскольку вы проверяете пул каждый раз, когда создаете максимальный объем памяти, который будут использовать маркеры, он будет на самом деле меньше, чем каждый раз при создании новых объектов (GC не удаляет сразу)

Рендеринг

2D-контекст - это отличный API для рендеринга, но он плохо используется, и вы можете убить частоту кадров, не изменяя визуализированный вид.

Если у вас много объектов, которые используют один и тот же стиль.Не отображайте их как отдельный путь.Определив один путь, добавьте объекты, затем заполните и обведите.

Пример

Пример пула быстрого огня.Все пули имеют одинаковый стиль.Этот интерфейс скрывает маркеры от основного кода.Вы можете получить доступ только к API пули

const bullets = (() => { // a singleton
    function Bullet() { }
    const radius = 5;
    const startLife = 100;
    this.radius = 5;
    const style = {
        fillStyle : "orange",
        strokeStyle : "green",
        lineWidth : 2,
    }    
    // to hold the interface 
    Bullets.prototype = {
        init(x,y,dx,dy) { // dx,dy are delta
            this.life = startLife;
            this.x = x;
            this.y = y;
            this.dx = dx;
            this.dy = dy;
        },
        draw() {
            ctx.arc(this.x, this.y, radius, 0 , Math.PI * 2);
        },
        move() {
            this.x += this.dx;
            this.y += this.dy;
            this.life --;
        }
    };
    const pool = []; // holds unused bullets
    const bullets = []; // holds active bullets
    // The API that manages the bullets
    const API = {
        fire(x,y,dx,dy) {
            var b;
            if(pool.length) {
                b = bullets.pop();
            } else {
                b = new Bullet();
            }               
            b.init(x,y,dx,dy);
            bullets.push(bullets); // put on active array
        },
        update() {
            var i;
            for(i = 0; i < bullets.length; i ++) {
                const b = bullets[i];
                b.move();
                if(b.life <= 0) { // is the bullet is no longer needed move to the pool
                    pool.push(bullets.splice(i--, 1)[0]);
                }
            }            
        },
        draw() {
            ctx.lineWidth = style.lineWidth;
            ctx.fillStyle = style.fillStyle;
            ctx.strokeStyle = style.strokeStyle;
            ctx.beginPath();
            for(const b of bullets) { b.draw() }
            ctx.fill();
            ctx.stroke();            
        },
        get count() { return bullets.length }, // get the number of active
        clear() { // remove all 
            pool.push(...bullets); // move all active to the pool;
            bullets.length = 0; // empty the array;
        },
        reset() { // cleans up all memory
            pool.length = 0;
            bullets.length = 0;
        }
    };
    return API;
})();

Для использования

... в функции огня

// simple example
bullets.fire(gun.x, gun.y, gun.dirX, gun.dirY);

... в главном цикле рендеринга

bullets.update(); // update all bullets
if(bullets.count) { // if there are bullets to draw
    bullets.draw();
}        

... если перезапустить уровень

bullets.clear(); // remove bullets from previouse play

... если в конце уровня освободить память

bullets.clear();    

Где-то между объектом.

Это объекты, которые лежат где-то посередине между двумя вышеуказанными типами,

Например, бонусы, фоновые предметы, агенты ИИ противника.

Если объект не создаетсяв больших количествах, и иметь жизни, которые могут длиться от секунды до менее полного уровня, вам нужно убедиться, что они могут быть созданы с использованием оптимального метода.(Лично я использую пулы (пузырьковый массив) для всех, кроме объектов, которые живут для жизни уровня, но это может привести к большому количеству кода)

Определить прототип

Существует два способа эффективного создания этих объектов.Используя синтаксис класса (лично ненавижу это дополнение к JS) или определите прототип вне функции создания экземпляра.

Пример

function Gun(player, bullets) {
    this.owner = player;
    this.bullets = bullets; // the bullet pool to use.
    this.x = player.x + player.size / 2 + 10;
    this.y = player.y + player.size / 2;

    this.width = 20;
    this.height = 10;
    const style = {
        fillStyle : "grey",
        strokeStyle : "brown",
        lineWidth : 1,
    };   
}

// Moving the API to the prototype improves memory use and makes creation a little quicker
Gun.prototype = {
    update() {
        this.x = this.owner.x + this.owner.size / 2 + 10;
        this.y = this.owner.y + this.owner.size / 2;
    },
    draw() {
        ctx.lineWidth = this.style.lineWidth;
        ctx.fillStyle = this.style.fillStyle;
        ctx.strokeStyle = this.style.strokeStyle;
        ctx.beginPath();
        ctx.rect(this.x,this.y,this.width,this.height);
        ctx.fill();
        ctx.stroke();
    },
    shoot() {
        this.bullets.fire(this.x, this.y, 10, 0);
    },
}    

Надеюсь, это поможет.:)

0 голосов
/ 01 января 2019

Проблема в том, что вы сначала рисуете фигуру, а затем вы устанавливаете заливку и обводку.При этом вы устанавливаете заливку и обводку для следующей фигуры.

В моем коде я использую ctx.translate(0,-400), потому что в противном случае холст был бы слишком большим.Удалите эту строку при настройке размера холста.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
//setting the canvas size
canvas.width = 400;
canvas.height = 200;

ctx.translate(0,-400);

function Shooter() {
    this.x = 100;
    this.y = 500;
    this.size = 50;
    this.color = "blue";
    this.borderColor = "black";
    this.borderWidth = 5;
    this.draw = function() {
    // first set the colors for this shape
    ctx.fillStyle = this.color;
    ctx.strokeStyle = this.borderColor;
    ctx.lineWidth = this.borderWidth;
    // then fill and stroke the shape  
    ctx.fillRect(this.x, this.y, this.size, this.size);
    ctx.strokeRect(this.x, this.y, this.size, this.size);
    }
}

function Gun() {
    this.x = sh.x + sh.size / 2 + 10;
    this.y = sh.y + sh.size / 2;
    this.color = "grey";
    this.borderColor = "brown";
    this.borderWidth = 1;
    this.width = 20;
    this.height = 10;
    this.draw = function() {
    ctx.fillStyle = this.color;
    ctx.strokeStyle = this.borderColor;
    ctx.lineWidth = this.borderWidth;
    ctx.fillRect(this.x,this.y,this.width,this.height);     ctx.strokeRect(this.x,this.y,this.width,this.height);

    }
}

function Bullet() {
    this.x = sh.x + sh.size * 2;
    this.y = sh.y + sh.size / 2;
    this.color = "orange";
    this.radius = 5;
    this.vx = 20;
    this.borderColor = "green";
    this.borderWidth = 2;
    this.draw = function() {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
        ctx.fillStyle = this.color;
        ctx.strokeStyle = this.borderColor;
        ctx.lineWidth = this.borderWidth;
        ctx.fill();
        ctx.stroke();
      
    }
}
var sh = new Shooter();
var g = new Gun();
var b = new Bullet();

function draw() {
  
    sh.draw();

    g.draw();
    
    b.draw();
    
requestAnimationFrame(draw);
}

draw();
canvas{border:1px solid}
<canvas id="canvas"></canvas>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...