Игра на JS эффект ночи
Всем привет. Решил я вообщем сделать свою небольшую игрушку, ничего особенного простенькая бродилка по карте из двухмерного массива с индексами координат спрайтов для отрисовки текстур ландшафта.
И вот стало мне интересно, как можно реализовать в игре эффект смены времени суток? Что бы вся карта очень сильно затемнялась, но в некоторых точках которые я определю на оси X и Y (например у фонаря или в точке нахождения персонажа), был небольшой радиус подсвечивающий ближайшую местность. Хочу добиться вот такого эффекта как на этих скриншотах! https://www.google.ru/imgres?imgurl=...&iact=c&ictx=1 https://www.google.ru/imgres?imgurl=...&iact=c&ictx=1 https://www.google.ru/imgres?imgurl=...&iact=c&ictx=1 Буду благодарен за помощь! |
я так полагаю делаете используя обычные html элементы?
|
Вложений: 1
Цитата:
В принципе я немного продвинулся по этому направлению, написал для персонажа функцию нахождения дистанции объекта относительно персонажа и установил переменную с радиусом света, если дистанция больше, тогда поверх фона отрисовываю новый слой rgba(0,0,0,0.8) если же радиус меньше то добавляю легкое затемнение, вроде работает var map = location.getMap("bg"); for (var i = 0 ; i < map.length; i++) { for (var j = 0; j < map[i].length; j++) { var x = (j * cellSize) + offset_map.x, y = (i * cellSize) + offset_map.y; if(character.getDistanseTo({x: x + (cellSize / 2), y: y + (cellSize / 2)}) >= character.getRadiusLight()) { ctx.fillStyle = "rgba(0,0,0,0.8)"; ctx.fillRect(x, y, cellSize, cellSize); } else { ctx.fillStyle = "rgba(0,0,0,0.3)"; ctx.fillRect(x, y, cellSize, cellSize); } } } Но есть некоторые недочеты и тут так как вся система карты у меня построена на сетке 32х32 так и отрисовка немного "квадратная" получается... Вот думаю как исправить. Если кто подскажет красивую формулу вычета разницы между этими отступами и закрашивания соседних квадратов ровно по линии радиуса персонажа, буду безмерно благодарен! |
canvas круг света
Arhitector,
мысли вслух ... <!DOCTYPE HTML> <html> <head> <title>Untitled</title> </head> <body> <canvas id="canvas" width="600" height="400" style="border:1px solid #d3d3d3;"></canvas> <script> var img = new Image; img.src = "https://mdn.mozillademos.org/files/5397/rhino.jpg"; img.onload = function() { draw(this); }; function draw(img) { var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); ctx.fillStyle = "black"; ctx.fillRect(0, 0, 600, 400); function move(event) { ctx.drawImage(img, 0, 0, 600, 400); var x = event.layerX; var y = event.layerY; var grd = ctx.createRadialGradient(x, y, 5, x, y, 100); grd.addColorStop(0, "transparent"); grd.addColorStop(1, "black"); ctx.fillStyle = grd; ctx.fillRect(0, 0, 600, 400); } canvas.addEventListener("mousemove", move); }; </script> </body> </html> |
Вложений: 2
Цитата:
Тут возникает немного другая проблема, когда вешаю данный код на функцию при наведением мыши которая запускается при попадании курсора на холст, всё работает замечательно. А вот когда пытаюсь привязать данное действие к координатам персонажа в функции которая запускается сразу и начинает зацикленно отрисовывать мою карту, вот тут сразу же игру ломает вот такая ошибка Uncaught TypeError: Failed to execute 'createRadialGradient' on 'CanvasRenderingContext2D': The provided double value is non-finite. at Object.drawMapBackground (draw.js:212) at render (game.js:178) at Image.water_bg.onload (game.js:211) function render () { // отрисовка ландшафта, объектов персонажа и тд тд тд draw.drawMapBackground(); // <<<<----- сюда requestAnimationFrame(render); // зацикливание отрисовки } var draw = { // код код код // drawMapBackground: function () { var real_pos = character.getRealCoordinates(); var grd = ctx.createRadialGradient(real_pos.x, real_pos.y, 10, real_pos.x, real_pos.y, r); grd.addColorStop(0, "transparent"); grd.addColorStop(1, "rgba(0,0,0,0.85)"); ctx.fillStyle = grd; ctx.fillRect(0, 0, example.width, example.height); } } С чем это может быть связано? Сам я что то не смог разобраться, гугловский перевод аля "Предоставляемое двойное значение не ограничено" как то совсем не помог увы :( |
Arhitector,
по обрывкам кода сложно что-то сказать, и выше была только идея, реализовать её можно по разному. |
Проверьте real_pos.x, real_pos.y и r.
Они должны быть числами(но не NaN или Infinite) и не строками. Возможно перед вызовом createRadialGradient можно проверить? if(!Number.isFinite(real_pos.x)) console.log("invalid real_pos.x"); if(!Number.isFinite(real_pos.y)) console.log("invalid real_pos.y"); if(!Number.isFinite(r)) console.log("invalid r"); |
Цитата:
И правда, совсем уже видимо замотался с этим делом, голова у меня не варит, переменные то проверить забыл, всё сразу встало на места. Я уже даже такую штуку написать успел... брр var r = character.getRadiusLight(), pos = character.getCoordinates(), normalDrawBG = function(object) { var r = object.r, x = object.x, y = object.y, bend = Math.round(object.r / Math.PI); ctx.fillStyle = "rgba(0,0,0,0.85)"; ctx.moveTo(x - r, y); ctx.bezierCurveTo(x - r, (y - r) - bend, x + r, (y - r) - bend, x + r, y); ctx.bezierCurveTo(x + r, y + r + bend, x - r, y + r + bend, x - r, y); }; ctx.beginPath(); normalDrawBG({x: pos.x, y: pos.y, r: r}); ctx.moveTo(0, 0); ctx.lineTo(0, canvas.height); ctx.lineTo(canvas.width, canvas.height); ctx.lineTo(canvas.width, 0); ctx.closePath(); ctx.fill(); |
Цитата:
|
Еще сразу тогда спрошу тут же, что бы темы не плодить. Можно ли добавить одновременно 2-3 и более таких эффектов на карту?
|
|
Arhitector,
Просто непересекаемых дырок с небольшим сглаживанием по краю наделать можно так: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <canvas id="canvas" width="400" height="250" style="border: 1px solid red"></canvas> <script> var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); class Light1 { constructor(x, y, radius) { this.x = x; this.y = y; this.radius = radius; } } class Dark1 { constructor(width, height) { this.width = width; this.height = height; this.lights = []; } draw(ctx) { ctx.save(); ctx.beginPath(); ctx.rect(0, 0, this.width, this.height); for (let light of this.lights) { ctx.moveTo(light.x, light.y); ctx.arc(light.x, light.y, light.radius, 0, 6.3); } ctx.shadowColor = 'rgba(0, 0, 0, 1)'; ctx.shadowBlur = 20; ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.fill('evenodd'); ctx.restore(); } } let dark1 = new Dark1(400, 250); dark1.lights.push(new Light1(50, 50, 30)); dark1.lights.push(new Light1(150, 100, 50)); dark1.lights.push(new Light1(100, 160, 70)); dark1.lights.push(new Light1(300, 150, 100)); let img = new Image; img.onload = function() { ctx.drawImage(img, 660, 290, 400, 250, 0, 0, 400, 250); dark1.draw(ctx); }; img.src = 'https://javascript.ru/forum/attachments/misc/3976d1534094922-igra-na-js-ehffekt-nochi-game-3-jpg'; </script> </body> </html> А так уже с градиентом наверное то что и нужно: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <canvas id="canvas" width="400" height="250" style="border: 1px solid red"></canvas> <script> var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); class Light2 { constructor(x, y, power, degree0 = 0, degree1 = 360) { this.x = x; this.y = y; this.radius0 = power * 50; this.radius1 = power * 100; let degree = Math.PI / 180; this.angle0 = degree0 * degree; this.angle1 = degree1 * degree; } } class Dark2 { constructor(width, height) { let offscreen = document.createElement('canvas'); this.ctx = offscreen.getContext('2d'); this.width = offscreen.width = width; this.height = offscreen.height = height; this.lights = []; } draw(ctx) { this.ctx.clearRect(0, 0, this.width, this.height); this.ctx.globalCompositeOperation = 'source-over'; this.ctx.fillStyle = 'rgba(0, 0, 0, 0.75)'; this.ctx.fillRect(0, 0, this.width, this.height); this.ctx.globalCompositeOperation = 'destination-out'; for (let light of this.lights) { this.ctx.moveTo(light.x, light.y); this.ctx.arc(light.x, light.y, light.radius1, light.angle0, light.angle1); let grd = this.ctx.createRadialGradient(light.x, light.y, light.radius0, light.x, light.y, light.radius1); grd.addColorStop(1, 'rgba(0, 0, 0, 0)'); grd.addColorStop(0, 'rgba(0, 0, 0, 0.75)'); this.ctx.fillStyle = grd; this.ctx.fill(); } ctx.drawImage(this.ctx.canvas, 0, 0); } } let dark2 = new Dark2(400, 250); dark2.lights.push(new Light2(50, 50, 0.3)); dark2.lights.push(new Light2(150, 100, 0.5)); dark2.lights.push(new Light2(100, 160, 0.7)); dark2.lights.push(new Light2(300, 150, 1, -150, -10)); let img = new Image; img.onload = function() { ctx.drawImage(img, 660, 290, 400, 250, 0, 0, 400, 250); dark2.draw(ctx); }; img.src = 'https://javascript.ru/forum/attachments/misc/3976d1534094922-igra-na-js-ehffekt-nochi-game-3-jpg'; </script> </body> </html> Формула радиуса освещения от мощности (или что там у света?) взята от балды. |
Rise,
Вот это уже почти идеально подходит! Огромное спасибо :) |
Теперь свет можно отдельно использовать:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <canvas id="canvas" width="400" height="250" style="border: 1px solid red"></canvas> <script> var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); class Light { constructor(x, y, power, degree0 = 0, degree1 = 360, color0 = 'rgba(255,255,255,0.75)', color1 = 'rgba(255,255,255,0)') { this.x = x; this.y = y; this.radius0 = power * 50; this.radius1 = power * 100; let degree = Math.PI / 180; this.angle0 = degree0 * degree; this.angle1 = degree1 * degree; this.color0 = color0; this.color1 = color1; } draw(ctx) { ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.arc(this.x, this.y, this.radius1, this.angle0, this.angle1); let grd = ctx.createRadialGradient(this.x, this.y, this.radius0, this.x, this.y, this.radius1); grd.addColorStop(0, this.color0); grd.addColorStop(1, this.color1); ctx.fillStyle = grd; ctx.fill(); } } class Dark { constructor(width, height, color = 'rgba(0,0,0,0.75)') { let offscreen = document.createElement('canvas'); this.ctx = offscreen.getContext('2d'); this.width = offscreen.width = width; this.height = offscreen.height = height; this.lights = []; this.color = color; } draw(ctx) { this.ctx.clearRect(0, 0, this.width, this.height); this.ctx.globalCompositeOperation = 'source-over'; this.ctx.fillStyle = this.color; this.ctx.fillRect(0, 0, this.width, this.height); this.ctx.globalCompositeOperation = 'destination-out'; for (let light of this.lights) light.draw(this.ctx); ctx.drawImage(this.ctx.canvas, 0, 0); } } let dark = new Dark(100, 250); dark.lights.push(new Light(50, 50, 0.3)); dark.lights.push(new Light(100, 160, 0.7, 90, 270)); let light = new Light(100, 160, 0.7, -90, 90); let sun = new Light(350, 50, 0.5, 0, 360, 'rgba(255, 255, 0, 1)', 'rgba(255, 0, 0, 0.1)'); let img = new Image; img.onload = function() { ctx.drawImage(img, 660, 290, 400, 250, 0, 0, 400, 250); dark.draw(ctx); light.draw(ctx); sun.draw(ctx); }; img.src = 'https://javascript.ru/forum/attachments/misc/3976d1534094922-igra-na-js-ehffekt-nochi-game-3-jpg'; </script> </body> </html> |
Часовой пояс GMT +3, время: 23:06. |