[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Зацените игру Arcanoid на JS
bodja
Чесно ,никогда не писал раньше игрушек,так что не бейте сильно. :D
Хотя я уже тут давненько ,и многим успел наступить на любимый мозоль,так что чую дело пахнет керосином. :ph34r:

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

Игрушка - "классика",ничего нового ,выложил для оценки не сколько ее ,сколько саму возможность реализации подобных вещей на
на обычном JS.
Примечательно то ,что код вышел на удивление небольшим ,учитывая функционал и динамику этой игры.
Ну и скорость работы весьма порадовала.
Так что ,думаю ,с JS не все настолько плачевно.
На все ,по все ушло 250 строчек кода и два вечера,
один на ваяние и рубание в цяцю и второй на причесывание кода,отлов мелких багов и оформление картинок.
Проверял на ИЕ6,опера и хром ,в ФФ непашет ,тупо не принимает события onkey,поэтому кнопки не пашут,можно
разрулить вопрос через window.event,но пока забил.

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

Игрушка здеся

PS Если кому интересно поиграться ,в коде можно добавлять сколько угодно уровней ,смотрим массив и располагаем кирпичи по своему усмотрению.

Свернутый текст

//Главный класс

function Arcanoid() {
//Инициализация платформы
this.platformX = 250;
this.platformY = 550;
this.platformW = 100;
this.platformStep = 0;
this.platform = document.createElement('div');
this.platform.style.cssText = 'position:absolute;width:' + this.platformW + 'px;height:10px;top:' + this.platformY + 'px;left:' + this.platformX + 'px;border:solid 1px #888;background:url("./platform.png");overflow:hidden;';
document.getElementById('game').appendChild(this.platform);

//Инициализация счетчика
this.counter = document.createElement('span');
this.counter.style.cssText = 'position:absolute;width:auto;height:auto;top:570px;left:90px;font:bold 24px Arial;color:#fff';
this.counter.innerHTML = 0;
document.getElementById('game').appendChild(this.counter);

//Инициализация жизней
this.life = document.createElement('span');
this.life.style.cssText = 'position:absolute;width:auto;height:auto;top:570px;left:340px;font:bold 24px Arial;color:#fff';
this.life.innerHTML = 3;
document.getElementById('game').appendChild(this.life);

//Инициализация индикатора уровней
this.lev = 1;
this.level = document.createElement('span');
this.level.style.cssText = 'position:absolute;width:auto;height:auto;top:570px;left:550px;font:bold 24px Arial;color:#fff';
this.level.innerHTML = this.lev;
document.getElementById('game').appendChild(this.level);

//Инициализация шарика
this.pause = true;
this.ballY = 540;
this.ballX = 295;
this.ballStepX = parseInt(Math.random() * 6) - 4;
this.ballStepY = -parseInt(Math.random() * 3 + 6);
this.ball = document.createElement('div');
this.ball.style.cssText = 'position:absolute;width:10px;height:10px;top:' + this.ballY + 'px;left:' + this.ballX + 'px;background:url("./ball.png");overflow:hidden;';
document.getElementById('game').appendChild(this.ball);

//Массив для обьектов кирпичей
this.bricks = [[], [], [], [], []];

//Обработчик события нажатия клавиш "пробел", "вправо", "влево"
document.body.onkeydown = function (o) {
return function () {
if (event.keyCode == 32) {
if (o.pause) {
o.pause = false;
o.ballCycles();
} else {
o.pause = true;
}
}

if (event.keyCode == 37) {
if (o.platformStep == 0) {
o.platformStep = -8;
o.platformCycles();
}
}

if (event.keyCode == 39) {
if (o.platformStep == 0) {
o.platformStep = 8;
o.platformCycles();
}
}
}
}

(this);

//Обработчик события отпускания клавиш
document.body.onkeyup = function (o) {
return function () {
if (event.keyCode == 37 || event.keyCode == 39) {
o.platformStep = 0;
}
}
}

(this);

//Обработчик циклов перемещения платформы
this.platformCycles = function () {
if (this.platformStep == -8 && this.platformX > 0) {
this.platformX += this.platformStep;
}
if (this.platformStep == 8 && this.platformX < 600 - this.platformW) {
this.platformX += this.platformStep;
}
if (this.platformStep != 0) {
this.platform.style.left = this.platformX + 'px';
setTimeout(function (o) {
return function () {
o.platformCycles();
}
}

(this), 20);
}
}


//Обработчик циклов перемещения шарика
this.ballCycles = function () {
//Меняем напрравление шарика при касании верхней границы поля
this.ballY += this.ballStepY;
if (this.ballY < 0) {
this.ballStepY *= -1;
this.ballY += 1;
}
//Проверка касания шарика с платформой,меняем направление,меняем скорость и вектор если платформа в этот момент движется
if (this.ballY > 540 && this.ballY < 560 && this.platformX < this.ballX && (this.platformX + this.platformW) > this.ballX) {
this.ballStepY *= -1;
this.ballStepX += this.platformStep / 2;
if (this.platformStep != 0) {
this.ballStepY -= 3;
}
}

this.ball.style.top = this.ballY + 'px';
this.ballX += this.ballStepX;
//Меняем направление шарика при касании боковых стенок поля
if (this.ballX < 0 || this.ballX > 590) {
this.ballStepX *= -1;
}
this.ball.style.left = this.ballX + 'px';
//Вычисляем касание шарика с кирпичом,меняем направление,запускаем обьект бонуса
var y = parseInt((this.ballY - 15) / 20);
var x = parseInt((this.ballX + 5) / 60);
if ((x >= 0) && (x < 10) && (y >= 0) && (y < 5) && (level[this.lev][y][x] != 0)) {
this.ballStepY *= -1;
level[this.lev][y][x] -= 1;
if (this.ballStepY > 4) {
this.ballStepY -= 1;
}
if (this.ballStepY < -4) {
this.ballStepY += 1;
}
this.counter.innerHTML = parseInt(this.counter.innerHTML) + 50;
this.bricks[y][x].brick.style.background = 'url("' + imgbrick[level[this.lev][y][x]] + '")';
//Проверяем "добили" ли мы кирпич и вызываем обьект бонуса
if (level[this.lev][y][x] == 0) {
document.getElementById('game').removeChild(this.bricks[y][x].brick);
this.counter.innerHTML = parseInt(this.counter.innerHTML) + 100;
this.bricks[y][x].bonus.open();
//Здесть вычисляем, убили ли мы все кирпичи
var end = 0;
for (var y = 0; y < 5; y++) {
for (var x = 0; x < 10; x++) {
end += level[this.lev][y][x];
}
}

//Если убили все ,переходи на следующий уровень и заново инициализируем обьекты кирпичей и шарик ставим в начальную позицию
if (end == 0) {
this.pause = true;
this.lev += 1;
this.level.innerHTML = this.lev;
if (this.lev + 1 > level.length) {
document.body.onkeydown = '';
alert('Поздравляем! Вы прошли все уровни. Ваши очки ' + this.counter.innerHTML);
} else {
alert('следующий уровень');
}
this.ballY = 540;
this.ballX = 250;
this.ballStepY = -5;
this.ball.style.cssText = 'position:absolute;background:url("./ball.png");width:10px;height:10px;top:' + this.ballY + 'px;left:' + this.ballX + 'px;overflow:hidden;';
this.open();
}
}
}

//Проверяем касание нижней границы,если жизни закончились ,прекращаем игру ,иначе ставим шарик в начальную позицию
if (this.ballY > 590) {
this.pause = true;
this.life.innerHTML = parseInt(this.life.innerHTML) - 1;
if (parseInt(this.life.innerHTML) == 0) {
document.body.onkeydown = '';
alert('Игра окончена!');
} else {
this.ballY = 540;
this.ballX = 295;
this.ballStepX = parseInt(Math.random() * 10) - 10;
this.ballStepY = parseInt(Math.random() * 5) - 8;
this.ball.style.cssText = 'position:absolute;background:url("./ball.png");width:10px;height:10px;top:' + this.ballY + 'px;left:' + this.ballX + 'px;overflow:hidden;';
}
}

//Проверка паузы ,если нет запускаем заново весь цикл
if (!this.pause) {
setTimeout(function (o) {
return function () {
o.ballCycles()
}
}

(this), 20);
}
}


//Инициализация обьектов кирпичей
this.open = function () {
for (var y = 0; y < 5; y++) {
for (var x = 0; x < 10; x++) {
if (level[this.lev][y][x] != 0) {
this.bricks[y][x] = new Brick(level[this.lev][y][x], x, y, this);
this.bricks[y][x].bonus.set = function (o, y, x) {
return function () {
o.counter.innerHTML = parseInt(o.counter.innerHTML) + 500;
if (o.bricks[y][x].bonus.star == 9) {
o.life.innerHTML = parseInt(o.life.innerHTML) + 1
}
if (o.bricks[y][x].bonus.star == 7) {
if (o.platformW == 100) {
o.platformW = 200;
o.platformX -= 50;
o.platform.style.left = o.platformX + 'px';
o.platform.style.width = '200px';
}
}
}
}

(this, y, x);
}
}
}
}

this.open();
}

//Класс кирпича,инициализация

function Brick(lev, posX, posY, obj) {
this.lev = lev;
this.X = 60 * posX;
this.Y = 20 * posY;
this.brick = document.createElement('div');
this.brick.style.cssText = 'position:absolute;background:url("' + imgbrick[this.lev] + '");width:58px;height:18px;top:' + (this.Y + 20) + 'px;left:' + this.X + 'px;overflow:hidden;';
document.getElementById('game').appendChild(this.brick);
this.bonus = new Bonus(posX, posY, obj);
}

//Класс бонуса для кирпича

function Bonus(posX, posY, obj) {
this.obj = obj;
this.star = parseInt(Math.random() * 10);
var bg = '#000';
var txt = '#000';
if (this.star == 3) {
bg = '#f88';
txt = '500';
}
if (this.star == 7) {
bg = '#8f8';
txt = '<>';
}
if (this.star == 9) {
bg = '#88f';
txt = '+';
}
this.X = 60 * posX;
this.Y = 20 * posY;
this.platformX = 0;
//Инициализация элемента отображения бонуса
this.bonus = document.createElement('div');
this.bonus.style.cssText = 'position:absolute;background:' + bg + ';width:58px;height:18px;top:' + (this.Y + 20) + 'px;left:' + this.X + 'px;font:bold 13px Arial;color:#000;text-align:center;';
this.bonus.innerHTML = txt;

//Инициализация метода касания бонуса с платформой,мы потом перегружаем его в главном классе
this.set = function () {};

//Располагаем элемент отображения бонуса на поле и запускаем цикл перемещения бонуса
this.open = function () {
if (this.star == 3 || this.star == 7 || this.star == 9) {
document.getElementById('game').appendChild(this.bonus);
this.cycles();
}
}


//Цикл перемещения бонуса и проверка на касание с платформой
this.cycles = function () {
this.Y += 5;
this.bonus.style.top = this.Y + 'px';
if (this.Y > 530 && this.Y < 550 && this.obj.platformX < (this.X + this.obj.platformW) && (this.obj.platformX + this.obj.platformW) > this.X) {
this.set();
document.getElementById('game').removeChild(this.bonus);
} else {
if (this.Y < 590) {
setTimeout(function (o) {
return function () {
o.cycles();
}
}

(this), 20);
} else {
document.getElementById('game').removeChild(this.bonus);
}
}
}
}


//Инициализация уровней игры ,0- нет кирпича,остальные значения - его прочность,5 рядов по 10 штук на каждый уровень
//добавляем сюда новую строчку массива-получаем новый уровень.

var level = [];
level[1] = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 5, 1, 1, 1, 1, 1, 1, 5, 0], [0, 5, 5, 5, 5, 5, 5, 5, 5, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 0]];
level[2] = [[0, 2, 1, 1, 5, 5, 1, 1, 2, 0], [0, 1, 2, 4, 0, 0, 4, 2, 1, 0], [5, 1, 1, 3, 3, 3, 3, 1, 1, 5], [0, 1, 2, 1, 1, 1, 1, 2, 1, 0], [0, 1, 1, 3, 3, 3, 3, 1, 1, 0]];
level[3] = [[0, 5, 0, 5, 0, 5, 0, 5, 0, 5], [5, 0, 5, 0, 5, 0, 5, 0, 5, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 2, 2, 2, 2, 2, 2, 2, 2, 0], [3, 3, 3, 3, 3, 3, 3, 3, 3, 3]];
level[4] = [[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 2, 2, 2, 0, 0, 2, 2, 2, 2], [5, 5, 5, 5, 1, 1, 5, 5, 5, 5]];
level[5] = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 5, 5, 5, 5, 5, 5, 5, 5, 0], [0, 5, 5, 5, 5, 5, 5, 5, 5, 0], [0, 5, 5, 5, 5, 5, 5, 5, 5, 0]];
level[6] = [[0, 0, 0, 0, 1, 1, 0, 0, 0, 0], [0, 0, 0, 2, 2, 2, 2, 0, 0, 0], [0, 0, 2, 2, 2, 2, 2, 2, 0, 0], [0, 3, 3, 3, 3, 3, 3, 3, 3, 0], [4, 4, 4, 4, 4, 4, 4, 4, 4, 4]];
level[7] = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 5, 1, 1, 1, 1, 1, 1, 5, 0], [0, 5, 1, 1, 1, 1, 1, 1, 5, 0], [0, 5, 5, 5, 5, 5, 5, 5, 5, 0]];

//картинка кирпича для каждого уровня прочности
var imgbrick = ['', './brick1.png', './brick2.png', './brick3.png', './brick4.png', './brick5.png']
var game;

//Запускаем главный класс после загрузки страницы.
window.onload = function () {
game = new Arcanoid();
}

// Вот и все :)








Спустя 10 минут, 24 секунды (6.08.2012 - 00:06) RCuPeR написал(а):
Замечательно !

Но был замечен баг: на большой скорости, если шарик падает под острым углом, он не отбивается, а скатывается по "отбойнику" в пропасть.

Спустя 3 минуты, 13 секунд (6.08.2012 - 00:09) Игорь_Vasinsky написал(а):
прикольно.

Спустя 31 минута, 25 секунд (6.08.2012 - 00:40) AlmazDelDiablo написал(а):
Вот это да! user posted image

Спустя 12 минут, 13 секунд (6.08.2012 - 00:53) inpost написал(а):
Я что-то не понял как запустить. На стрелки не реагирует...

Спустя 2 часа, 23 минуты, 22 секунды (6.08.2012 - 03:16) kamanch написал(а):
inpost
Цитата
в ФФ непашет ,тупо не принимает события onkey,поэтому кнопки не пашут


bodja
Классно, детсво вспомнил smile.gif

Спустя 7 часов, 30 минут, 12 секунд (6.08.2012 - 10:46) inpost написал(а):
kamanch
Вот тут надо бы разобраться, потому что у меня в ФФ моя игрушка пашет, все кнопки работают.

Спустя 10 часов, 5 минут, 18 секунд (6.08.2012 - 20:52) bodja написал(а):
Всем спасибо за отзывы. biggrin.gif rolleyes.gif
Думаю можно положить в портфель работ.

RCuPeR
Да есть такой баг ,уже профиксил.
При написании игры столкнулся с таким интересным для себя явлением ,как баги в поведении кода,тоесть код пашет, ничего не сыпется ,но ведет себя странно.Например фиксил баг,когда
шарик выходил в полностью горизонтальную траекторию и бесконечно бился по боковым стенкам biggrin.gif , так же ставил ограничитель минимальной скорости ,что бы шарик не остановился где нибудь по середине поля.

inpost
в этой игре в ФФ непашет ,может позже сделаю,
но а если вы про свою ,ну то я ее пока невидел.

Спустя 10 минут, 2 секунды (6.08.2012 - 21:02) sharki написал(а):
bodja
Молодца, мне понравилось, и код на уровне

Спустя 23 минуты, 28 секунд (6.08.2012 - 21:25) inpost написал(а):
bodja
http://nbflower.ru/test/runner.php , не доделал, правда, забил по причине невозможности толком делать одновременную прокрутку по двум осям без лагов (плавно). Ну, правда, если любопытно :) Да и слишком требовательна к ресурсам, чем больше вкладок открыто, тем хуже работает! Если вкладок ни одной другой в том же хроме - работает нормально, иначе надо корректировать скорость. А вот это самое ужасное, ведь скорость будет работать от нагрузки, нельзя четкую выставить с интервалами...

e = event || window.event
if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which;


Так в мануале на javascript написано, применил и работает везде.

Спустя 21 час, 24 минуты, 9 секунд (7.08.2012 - 18:49) bodja написал(а):
Немного сузил проблему ,получается ФФ хочет при событии передачу аргумента event а ИЕ наоборот нехочет.
типа такого function (event){}
в итоге в ФФ пашет ,а в ИЕ нет smile.gif
Думаю пока ,как разрулить.

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

sharki

спасибо smile.gif

Спустя 15 минут, 40 секунд (7.08.2012 - 19:05) alex12060 написал(а):
Я б еще, на твоем месте, сделал ограничение скорости шарика.
А то при быстрой скорости после каждого удара он молниеносно ее набирал, что я тупо не успевал доехать до него

Спустя 26 минут, 21 секунда (7.08.2012 - 19:31) bodja написал(а):
alex12060
Просто не нужно его разгонять ,если платформа скользит в момент удара -шарик разгоняется,если будет стоять -нет.
При каждов ударе по кирпичу у шарика медленно падает скорость.

Спустя 36 минут, 9 секунд (7.08.2012 - 20:07) Игорь_Vasinsky написал(а):
bodja
вот же инпост написал кроссбраузерный вариант
e = event || window.event
if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which;

Спустя 1 час, 37 минут, 57 секунд (7.08.2012 - 21:45) bodja написал(а):
Игорь_Vasinsky

повесте это на событие
document.body.onkeydown
document.body.onkeyup
window.onkeydown
window.onkeyup

можно примерно так
window.onkeyup=function (event){
e = event || window.event
if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which;
alert(code);
}

и проверте в ИЕ :D
У меня в ИЕ6 не пашет,если заработает этот код в ИЕ7+ вопрос будет снят.

Спустя 3 минуты, 47 секунд (7.08.2012 - 21:49) Игорь_Vasinsky написал(а):
а не так?

e = event || window.event;

window.onkeyup=function (e){

if (e.keyCode) code = e.keyCode; else if (e.which) code = e.which;
alert(code);
}

Спустя 3 минуты, 57 секунд (7.08.2012 - 21:53) bodja написал(а):
фигушки,хотя по идее должен.

Спустя 6 минут, 20 секунд (7.08.2012 - 21:59) Игорь_Vasinsky написал(а):
а какую ошибку консоль кажет?

Спустя 1 минута, 5 секунд (7.08.2012 - 22:00) Игорь_Vasinsky написал(а):
тогда аргумент не нужен.

Спустя 4 минуты, 29 секунд (7.08.2012 - 22:05) bodja написал(а):
Игорь_Vasinsky
Вообще ,никакую, что в ФФ что в ИЕ,вот и грешил вначале,что у ФФ события не пашут.
Если убрать аргумент ,не будет работать в ФФ biggrin.gif
Тут походу нужно разруливать браузеры и посовывать нужные варианты функций.

Спустя 4 минуты, 2 секунды (7.08.2012 - 22:09) Игорь_Vasinsky написал(а):
да. так и делают.

а лучше сделай линк на гугл хром в приветствии в игре. чтобы не пользовались всякой хернёй laugh.gif

Спустя 3 минуты, 7 секунд (7.08.2012 - 22:12) Winston написал(а):
Цитата (Игорь_Vasinsky @ 7.08.2012 - 23:09)
а лучше сделай линк на гугл хром в приветствии в игре. чтобы не пользовались всякой хернёй 

+1

Спустя 12 минут, 1 секунда (7.08.2012 - 22:24) inpost написал(а):
bodja
А моя игра в ИЕ7+ работает? ИЕ9 точно работает, что насчёт предыдущих версий?

Спустя 4 минуты, 22 секунды (7.08.2012 - 22:28) Игорь_Vasinsky написал(а):
да лан)) начинаешь *ськами меряться. biggrin.gif

я ж предложил кроссбраузерное решение. всего - то - признать гугл богом и поклоняться ему. я язычников жечь.


как то я решал этот вопрос, но IE6 я точно не трогал. заразиться боюсь и вообще - люди с IE6 - это что то не объяснимое. - типа служба installer в windows отключена biggrin.gif

Спустя 12 часов, 17 минут, 22 секунды (8.08.2012 - 10:46) bodja написал(а):
inpost
В ИЕ6 полные дрова :)
rotate точно не будет работать в ИЕ6,7 и 8.

Игорь_Vasinsky
Да ладно,ваш хром тоже хорош.
У него событие onkeydown срабатывает постоянно при удержании кнопки ,хотя по идее должно сработать один раз ,а остальное передать onkeypress. Из за чего пришлось сделать блокировку повторных вызовов функции перемещения платформы.

ЗЫ Вот вроде ближе к кроссбраузерному решению.
Передачу аргумента event ИЕ схавал в событиях document.body.onkeydown и document.body.onkeyup но назначать нужно только после загрузки страницы.

window.onload=function(){
document.body.onkeydown=function (event) {
var e = event || window.event;
alert(e.keyCode);
}
}
}

Спустя 3 минуты, 28 секунд (8.08.2012 - 10:49) Игорь_Vasinsky написал(а):
FF вроде keycode не понимает

вот


var Key = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;

Спустя 5 минут, 25 секунд (8.08.2012 - 10:55) bodja написал(а):
Игорь_Vasinsky
Ну так откройте игрушку в ФФ поиграйте biggrin.gif ,12 и 14 версии работают.По древним ,нужно смотреть ,вопрос отдельный.

Спустя 1 минута, 43 секунды (8.08.2012 - 10:56) Игорь_Vasinsky написал(а):
) сне некогда, помню просто писал кроссбраузерный JS валидатор, том и разнюхал http://vasinsky.ru/callback/

Спустя 13 минут, 48 секунд (8.08.2012 - 11:10) bodja написал(а):
Комбинацию с e.which ИЕ6 опять не схавал,ладно,надоело уже эксперементировать.
Пойду inpost -у помогу с игрушкой ,пока настроение есть.

Спустя 2 дня, 2 часа, 22 минуты, 42 секунды (10.08.2012 - 13:33) eaborodkin написал(а):
плохо что бонусы, когда шарик упустил, не сгорают...
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.