I'm yakin ini adalah pelarut 1000 kali sebelum: saya punya kanvas dalam ukuran 960560 dan kamar ukuran 50003000 yang selalu hanya 960*560 harus diambil, tergantung pada mana pemain. Pemain harus selalu di tengah, tapi ketika dekat dengan perbatasan - maka tampilan terbaik harus dihitung). Pemain bisa bergerak sepenuhnya gratis dengan WASD atau tombol panah. Dan semua benda harus bergerak sendiri - bukan yang saya memindahkan segala sesuatu yang lain tapi pemain untuk menciptakan ilusi bahwa pemain bergerak.
Sekarang saya menemukan dua quesitons:
https://stackoverflow.com/questions/11464550/html5-creating-a-viewport-for-canvas bekerja, tapi hanya untuk jenis permainan, saya bisa't mereproduksi kode untuk tambang.
https://stackoverflow.com/questions/8592872/changing-the-view-center-of-an-html5-canvas tampaknya lebih menjanjikan dan juga perfomant, tapi aku hanya mengerti untuk menggambar semua benda-benda lain yang benar relatif terhadap pemain dan tidak berapa untuk scroll kanvas pandang relatif terhadap pemain, yang ingin saya capai pertama tentu saja.
Kode saya (modern - permainan logika secara terpisah):
var canvas = document.getElementById("game");
canvas.tabIndex = 0;
canvas.focus();
var cc = canvas.getContext("2d");
// Define viewports for scrolling inside the canvas
/* Viewport x position */ view_xview = 0;
/* Viewport y position */ view_yview = 0;
/* Viewport width */ view_wview = 960;
/* Viewport height */ view_hview = 560;
/* Sector width */ room_width = 5000;
/* Sector height */ room_height = 3000;
canvas.width = view_wview;
canvas.height = view_hview;
function draw()
{
clear();
requestAnimFrame(draw);
// World's end and viewport
if (player.x < 20) player.x = 20;
if (player.y < 20) player.y = 20;
if (player.x > room_width-20) player.x = room_width-20;
if (player.y > room_height-20) player.y = room_height-20;
if (player.x > view_wview/2) ... ?
if (player.y > view_hview/2) ... ?
}
Cara saya mencoba untuk membuatnya bekerja merasa benar-benar salah dan saya don't bahkan tahu bagaimana saya mencoba itu... Ada ide? Apa yang anda pikirkan tentang konteks.transform-hal?
Saya harap anda mengerti penjelasan saya dan seseorang yang memiliki ide. Salam
[DEMO](
) di jsfiddle.net Demo ini menggambarkan pandang penggunaan dalam permainan nyata skenario. Gunakan tombol panah untuk memindahkan pemain di atas kamar. Ruangan besar yang dihasilkan pada terbang menggunakan persegi panjang dan hasilnya disimpan ke dalam sebuah gambar. Melihat bahwa pemain ini selalu di tengah kecuali bila dekat dengan perbatasan (sesuai keinginan anda).Sekarang saya'll mencoba untuk menjelaskan bagian-bagian utama dari kode, setidaknya bagian-bagian yang lebih sulit untuk mengerti hanya dengan melihat saja.
Varian dari drawImage metode memiliki delapan parameter baru. Kita dapat menggunakan metode ini untuk mengiris bagian-bagian dari gambar sumber dan menarik mereka ke kanvas.
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) Parameter pertama gambar, sama seperti varian lainnya, adalah salah satu referensi untuk sebuah objek gambar atau referensi ke berbagai elemen kanvas. Untuk delapan lainnya parameter ini's terbaik untuk melihat gambar di bawah ini. Empat parameter pertama mendefinisikan lokasi dan ukuran dari potongan pada gambar sumber. Empat parameter yang menentukan posisi dan ukuran pada tujuan kanvas. Font: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Using_images Bagaimana cara kerjanya di demo: Kita memiliki sebuah gambar besar yang mewakili ruang dan kami ingin menunjukkan pada kanvas hanya bagian dalam viewport. Posisi tanaman (sx, sy) adalah sama posisi kamera (xView, yView) dan tanaman dimensi yang sama sebagai area pandang(kanvas) jadi
sWidth=kanvas.lebar
dansHeight=kanvas.ketinggian
. Kita perlu berhati-hati tentang tanaman dimensi karenadrawImage
ada yang menarik di atas kanvas jika posisi tanaman atau tanaman dimensi berdasarkan posisi yang tidak valid. Yang's mengapa kita perlujika
bagian bawah.
var sx, sy, dx, dy;
var sWidth, sHeight, dWidth, dHeight;
// offset point to crop the image
sx = xView;
sy = yView;
// dimensions of cropped image
sWidth = context.canvas.width;
sHeight = context.canvas.height;
// if cropped image is smaller than canvas we need to change the source dimensions
if(image.width - sx < sWidth){
sWidth = image.width - sx;
}
if(image.height - sy < sHeight){
sHeight = image.height - sy;
}
// location on canvas to draw the croped image
dx = 0;
dy = 0;
// match destination with source to not scale the image
dWidth = sWidth;
dHeight = sHeight;
// draw the cropped image
context.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
Ketika menulis permainan ini's praktik yang baik memisahkan logika dan render untuk setiap objek dalam permainan. Jadi di demo kita harus update
dan menarik
fungsi. Update
metode perubahan objek status seperti posisi pada "permainan dunia", menerapkan fisika, animasi negara, dll. Menggambar
metode yang benar-benar membuat objek dan untuk membuat itu benar mengingat pandang, objek perlu tahu render konteks dan viewport properti.
Perhatikan bahwa objek permainan diperbarui mengingat dunia game's posisi. Yang berarti (x,y) posisi objek adalah posisi di dunia. Terlepas dari itu, sejak viewport berubah, benda harus diberikan dengan benar dan membuat posisi akan berbeda dari dunia's posisi.
Konversi sederhana:
posisi objek di dunia(kamar): (x, y)
viewport posisi: (xView, yView)
membuat posisi: (- x xView, y-yView)
Ini bekerja untuk semua jenis koordinat, bahkan negatif.
Benda permainan kami memiliki dipisahkan metode update. Dalam Demo implementasi, kamera diperlakukan sebagai objek permainan dan juga memiliki dipisahkan metode update.
Kamera objek memegang kiri atas posisi viewport (xView, yView)
, sebuah objek harus diikuti, persegi panjang yang mewakili pandang, persegi panjang yang mewakili dunia game's batas dan jarak minimal dari masing-masing perbatasan pemain yang bisa menjadi sebelum kamera mulai bergerak (xDeadZone, yDeadZone). Kami juga didefinisikan kamera's derajat kebebasan (axis). Untuk tampilan atas gaya permainan, seperti RPG, kamera ini memungkinkan untuk bergerak di kedua x(horizontal) dan y(vertikal) axis.
Untuk menjaga pemain di tengah pandang kita mengatur deadZone sumbu masing-masing untuk berkumpul dengan pusat kanvas. Lihatlah mengikuti fungsi dalam kode:
kamera.mengikuti(pemain, kanvas.lebar/2, canvas.tinggi/2) Catatan: Lihat UPDATE bagian bawah seperti ini tidak akan menghasilkan perilaku yang diharapkan ketika setiap dimensi dari peta (kamar) lebih kecil dari kanvas.
World's batas
Karena masing-masing objek, termasuk kamera, memiliki fungsi update, mudah untuk memeriksa permainan world's batas. Hanya ingat untuk menempatkan kode yang menghalangi gerakan di final fungsi update.
Demonstrasi
Melihat penuh kode dan mencobanya sendiri. Sebagian besar kode memiliki komentar yang memandu anda melalui. I'll berasumsi bahwa anda tahu dasar-dasar Javascript dan bagaimana untuk bekerja dengan prototipe (kadang-kadang saya menggunakan istilah "kelas" untuk prototipe objek hanya karena memiliki perilaku yang sama dari Kelas bahasa seperti bahasa Jawa). [DEMO](
) Penuh kode:
<!DOCTYPE HTML>
<html>
<body>
<canvas id="gameCanvas" width=400 height=400 />
<script>
// wrapper for our game "classes", "methods" and "objects"
window.Game = {};
// wrapper for "class" Rectangle
(function() {
function Rectangle(left, top, width, height) {
this.left = left || 0;
this.top = top || 0;
this.width = width || 0;
this.height = height || 0;
this.right = this.left + this.width;
this.bottom = this.top + this.height;
}
Rectangle.prototype.set = function(left, top, /*optional*/ width, /*optional*/ height) {
this.left = left;
this.top = top;
this.width = width || this.width;
this.height = height || this.height
this.right = (this.left + this.width);
this.bottom = (this.top + this.height);
}
Rectangle.prototype.within = function(r) {
return (r.left <= this.left &&
r.right >= this.right &&
r.top <= this.top &&
r.bottom >= this.bottom);
}
Rectangle.prototype.overlaps = function(r) {
return (this.left < r.right &&
r.left < this.right &&
this.top < r.bottom &&
r.top < this.bottom);
}
// add "class" Rectangle to our Game object
Game.Rectangle = Rectangle;
})();
// wrapper for "class" Camera (avoid global objects)
(function() {
// possibles axis to move the camera
var AXIS = {
NONE: 1,
HORIZONTAL: 2,
VERTICAL: 3,
BOTH: 4
};
// Camera constructor
function Camera(xView, yView, viewportWidth, viewportHeight, worldWidth, worldHeight) {
// position of camera (left-top coordinate)
this.xView = xView || 0;
this.yView = yView || 0;
// distance from followed object to border before camera starts move
this.xDeadZone = 0; // min distance to horizontal borders
this.yDeadZone = 0; // min distance to vertical borders
// viewport dimensions
this.wView = viewportWidth;
this.hView = viewportHeight;
// allow camera to move in vertical and horizontal axis
this.axis = AXIS.BOTH;
// object that should be followed
this.followed = null;
// rectangle that represents the viewport
this.viewportRect = new Game.Rectangle(this.xView, this.yView, this.wView, this.hView);
// rectangle that represents the world's boundary (room's boundary)
this.worldRect = new Game.Rectangle(0, 0, worldWidth, worldHeight);
}
// gameObject needs to have "x" and "y" properties (as world(or room) position)
Camera.prototype.follow = function(gameObject, xDeadZone, yDeadZone) {
this.followed = gameObject;
this.xDeadZone = xDeadZone;
this.yDeadZone = yDeadZone;
}
Camera.prototype.update = function() {
// keep following the player (or other desired object)
if (this.followed != null) {
if (this.axis == AXIS.HORIZONTAL || this.axis == AXIS.BOTH) {
// moves camera on horizontal axis based on followed object position
if (this.followed.x - this.xView + this.xDeadZone > this.wView)
this.xView = this.followed.x - (this.wView - this.xDeadZone);
else if (this.followed.x - this.xDeadZone < this.xView)
this.xView = this.followed.x - this.xDeadZone;
}
if (this.axis == AXIS.VERTICAL || this.axis == AXIS.BOTH) {
// moves camera on vertical axis based on followed object position
if (this.followed.y - this.yView + this.yDeadZone > this.hView)
this.yView = this.followed.y - (this.hView - this.yDeadZone);
else if (this.followed.y - this.yDeadZone < this.yView)
this.yView = this.followed.y - this.yDeadZone;
}
}
// update viewportRect
this.viewportRect.set(this.xView, this.yView);
// don't let camera leaves the world's boundary
if (!this.viewportRect.within(this.worldRect)) {
if (this.viewportRect.left < this.worldRect.left)
this.xView = this.worldRect.left;
if (this.viewportRect.top < this.worldRect.top)
this.yView = this.worldRect.top;
if (this.viewportRect.right > this.worldRect.right)
this.xView = this.worldRect.right - this.wView;
if (this.viewportRect.bottom > this.worldRect.bottom)
this.yView = this.worldRect.bottom - this.hView;
}
}
// add "class" Camera to our Game object
Game.Camera = Camera;
})();
// wrapper for "class" Player
(function() {
function Player(x, y) {
// (x, y) = center of object
// ATTENTION:
// it represents the player position on the world(room), not the canvas position
this.x = x;
this.y = y;
// move speed in pixels per second
this.speed = 200;
// render properties
this.width = 50;
this.height = 50;
}
Player.prototype.update = function(step, worldWidth, worldHeight) {
// parameter step is the time between frames ( in seconds )
// check controls and move the player accordingly
if (Game.controls.left)
this.x -= this.speed * step;
if (Game.controls.up)
this.y -= this.speed * step;
if (Game.controls.right)
this.x += this.speed * step;
if (Game.controls.down)
this.y += this.speed * step;
// don't let player leaves the world's boundary
if (this.x - this.width / 2 < 0) {
this.x = this.width / 2;
}
if (this.y - this.height / 2 < 0) {
this.y = this.height / 2;
}
if (this.x + this.width / 2 > worldWidth) {
this.x = worldWidth - this.width / 2;
}
if (this.y + this.height / 2 > worldHeight) {
this.y = worldHeight - this.height / 2;
}
}
Player.prototype.draw = function(context, xView, yView) {
// draw a simple rectangle shape as our player model
context.save();
context.fillStyle = "black";
// before draw we need to convert player world's position to canvas position
context.fillRect((this.x - this.width / 2) - xView, (this.y - this.height / 2) - yView, this.width, this.height);
context.restore();
}
// add "class" Player to our Game object
Game.Player = Player;
})();
// wrapper for "class" Map
(function() {
function Map(width, height) {
// map dimensions
this.width = width;
this.height = height;
// map texture
this.image = null;
}
// creates a prodedural generated map (you can use an image instead)
Map.prototype.generate = function() {
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = this.width;
ctx.canvas.height = this.height;
var rows = ~~(this.width / 44) + 1;
var columns = ~~(this.height / 44) + 1;
var color = "red";
ctx.save();
ctx.fillStyle = "red";
for (var x = 0, i = 0; i < rows; x += 44, i++) {
ctx.beginPath();
for (var y = 0, j = 0; j < columns; y += 44, j++) {
ctx.rect(x, y, 40, 40);
}
color = (color == "red" ? "blue" : "red");
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
ctx.restore();
// store the generate map as this image texture
this.image = new Image();
this.image.src = ctx.canvas.toDataURL("image/png");
// clear context
ctx = null;
}
// draw the map adjusted to camera
Map.prototype.draw = function(context, xView, yView) {
// easiest way: draw the entire map changing only the destination coordinate in canvas
// canvas will cull the image by itself (no performance gaps -> in hardware accelerated environments, at least)
/*context.drawImage(this.image, 0, 0, this.image.width, this.image.height, -xView, -yView, this.image.width, this.image.height);*/
// didactic way ( "s" is for "source" and "d" is for "destination" in the variable names):
var sx, sy, dx, dy;
var sWidth, sHeight, dWidth, dHeight;
// offset point to crop the image
sx = xView;
sy = yView;
// dimensions of cropped image
sWidth = context.canvas.width;
sHeight = context.canvas.height;
// if cropped image is smaller than canvas we need to change the source dimensions
if (this.image.width - sx < sWidth) {
sWidth = this.image.width - sx;
}
if (this.image.height - sy < sHeight) {
sHeight = this.image.height - sy;
}
// location on canvas to draw the croped image
dx = 0;
dy = 0;
// match destination with source to not scale the image
dWidth = sWidth;
dHeight = sHeight;
context.drawImage(this.image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
}
// add "class" Map to our Game object
Game.Map = Map;
})();
// Game Script
(function() {
// prepaire our game canvas
var canvas = document.getElementById("gameCanvas");
var context = canvas.getContext("2d");
// game settings:
var FPS = 30;
var INTERVAL = 1000 / FPS; // milliseconds
var STEP = INTERVAL / 1000 // seconds
// setup an object that represents the room
var room = {
width: 500,
height: 300,
map: new Game.Map(500, 300)
};
// generate a large image texture for the room
room.map.generate();
// setup player
var player = new Game.Player(50, 50);
// Old camera setup. It not works with maps smaller than canvas. Keeping the code deactivated here as reference.
/* var camera = new Game.Camera(0, 0, canvas.width, canvas.height, room.width, room.height);*/
/* camera.follow(player, canvas.width / 2, canvas.height / 2); */
// Set the right viewport size for the camera
var vWidth = Math.min(room.width, canvas.width);
var vHeight = Math.min(room.height, canvas.height);
// Setup the camera
var camera = new Game.Camera(0, 0, vWidth, vHeight, room.width, room.height);
camera.follow(player, vWidth / 2, vHeight / 2);
// Game update function
var update = function() {
player.update(STEP, room.width, room.height);
camera.update();
}
// Game draw function
var draw = function() {
// clear the entire canvas
context.clearRect(0, 0, canvas.width, canvas.height);
// redraw all objects
room.map.draw(context, camera.xView, camera.yView);
player.draw(context, camera.xView, camera.yView);
}
// Game Loop
var gameLoop = function() {
update();
draw();
}
// <-- configure play/pause capabilities:
// Using setInterval instead of requestAnimationFrame for better cross browser support,
// but it's easy to change to a requestAnimationFrame polyfill.
var runningId = -1;
Game.play = function() {
if (runningId == -1) {
runningId = setInterval(function() {
gameLoop();
}, INTERVAL);
console.log("play");
}
}
Game.togglePause = function() {
if (runningId == -1) {
Game.play();
} else {
clearInterval(runningId);
runningId = -1;
console.log("paused");
}
}
// -->
})();
// <-- configure Game controls:
Game.controls = {
left: false,
up: false,
right: false,
down: false,
};
window.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 37: // left arrow
Game.controls.left = true;
break;
case 38: // up arrow
Game.controls.up = true;
break;
case 39: // right arrow
Game.controls.right = true;
break;
case 40: // down arrow
Game.controls.down = true;
break;
}
}, false);
window.addEventListener("keyup", function(e) {
switch (e.keyCode) {
case 37: // left arrow
Game.controls.left = false;
break;
case 38: // up arrow
Game.controls.up = false;
break;
case 39: // right arrow
Game.controls.right = false;
break;
case 40: // down arrow
Game.controls.down = false;
break;
case 80: // key P pauses the game
Game.togglePause();
break;
}
}, false);
// -->
// start the game when page is loaded
window.onload = function() {
Game.play();
}
</script>
</body>
</html>
UPDATE
Jika lebar dan/atau tinggi dari peta (kamar) lebih kecil dari kanvas kode sebelumnya tidak akan bekerja dengan benar. Untuk mengatasi hal ini, dalam Permainan Script membuat setup kamera sebagai berikut:
// Set the right viewport size for the camera
var vWidth = Math.min(room.width, canvas.width);
var vHeight = Math.min(room.height, canvas.height);
var camera = new Game.Camera(0, 0, vWidth, vHeight, room.width, room.height);
camera.follow(player, vWidth / 2, vHeight / 2);
kamera.ikuti
fungsi harus update juga. Jangan ragu untuk melaporkan setiap kesalahan atau untuk menambahkan saran.
Kode pada jawaban yang diterima adalah sedikit banyak. Yang sederhana ini:
function draw() {
ctx.setTransform(1,0,0,1,0,0);//reset the transform matrix as it is cumulative
ctx.clearRect(0, 0, canvas.width, canvas.height);//clear the viewport AFTER the matrix is reset
//Clamp the camera position to the world bounds while centering the camera around the player
var camX = clamp(-player.x + canvas.width/2, yourWorld.minX, yourWorld.maxX - canvas.width);
var camY = clamp(-player.y + canvas.height/2, yourWorld.minY, yourWorld.maxY - canvas.height);
ctx.translate( camX, camY );
//Draw everything
}
Dan penjepit terlihat seperti:
function clamp(value, min, max){
if(value < min) return min;
else if(value > max) return max;
return value;
}
Berikut adalah cara untuk menggunakan kanvas untuk menjadi viewport lain yang lebih besar dari kanvas gambar
Viewport adalah benar-benar hanya dipotong sebagian besar gambar yang akan ditampilkan kepada pengguna.
Dalam hal ini, viewport akan ditampilkan kepada pengguna pada sebuah kanvas (kanvas viewport).
Pertama, kode memindahkan fungsi panci viewport seluruh gambar yang lebih besar.
Fungsi ini bergerak atas/kiri sudut pandang oleh 5px dalam arah tertentu:
function move(direction){
switch (direction){
case "left":
left-=5;
break;
case "up":
top-=5;
break;
case "right":
left+=5;
break;
case "down":
top+=5
break;
}
draw(top,left);
}
Pindahkan fungsi panggilan fungsi menarik.
Dalam menggambar(), yang drawImage
fungsi akan tanaman tertentu bagian dari gambar yang lebih besar.
drawImage
juga akan menampilkan yang "dipotong latar belakang" untuk pengguna di kanvas.
context.clearRect(0,0,game.width,game.height);
context.drawImage(background,cropLeft,cropTop,cropWidth,cropHeight,
0,0,viewWidth,viewHeight);
Dalam contoh ini,
Latar belakang penuh gambar latar belakang (biasanya tidak ditampilkan, tetapi lebih merupakan sumber untuk tanam)
cropLeft & cropTop menentukan mana pada gambar latar belakang tanam akan dimulai.
cropWidth & cropHeight menentukan seberapa besar persegi panjang akan dipotong dari gambar latar belakang.
0,0 mengatakan bahwa sub-citra yang telah dipotong dari latar belakang yang akan diundi pada 0,0 pada viewport kanvas.
viewWidth & viewHeight adalah lebar dan tinggi dari viewport kanvas
Jadi di sini adalah contoh dari drawImage menggunakan angka-angka.
Katakanlah kita pandang (= kami tampilan kanvas) adalah 150 piksel lebar dan 100 piksel tinggi.
context.drawImage(background,75,50,150,100,0,0,150,100);
75 & 50 mengatakan bahwa tanam akan dimulai pada posisi x=75/y=50 pada gambar latar belakang.
Yang 150,100 mengatakan bahwa persegi panjang dapat dipotong akan menjadi 150 dan lebar 100 tinggi.
Yang 0,0,150,100 mengatakan bahwa yang dipotong persegi panjang gambar akan ditampilkan dengan menggunakan full ukuran viewport kanvas.
Itu untuk mekanik menggambar pandang...hanya menambahkan kunci-kontrol!
Berikut adalah kode dan Biola:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var game=document.getElementById("game");
var gameCtx=game.getContext("2d");
var left=20;
var top=20;
var background=new Image();
background.onload=function(){
canvas.width=background.width/2;
canvas.height=background.height/2;
gameCtx.fillStyle="red";
gameCtx.strokeStyle="blue";
gameCtx.lineWidth=3;
ctx.fillStyle="red";
ctx.strokeStyle="blue";
ctx.lineWidth=3;
move(top,left);
}
background.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/game.jpg";
function move(direction){
switch (direction){
case "left":
left-=5;
break;
case "up":
top-=5;
break;
case "right":
left+=5;
break;
case "down":
top+=5
break;
}
draw(top,left);
}
function draw(top,left){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(background,0,0,background.width,background.height,0,0,canvas.width,canvas.height);
gameCtx.clearRect(0,0,game.width,game.height);
gameCtx.drawImage(background,left,top,250,150,0,0,250,150);
gameCtx.beginPath();
gameCtx.arc(125,75,10,0,Math.PI*2,false);
gameCtx.closePath();
gameCtx.fill();
gameCtx.stroke();
ctx.beginPath();
ctx.rect(left/2,top/2,125,75);
ctx.stroke();
ctx.beginPath();
ctx.arc(left/2+125/2,top/2+75/2,5,0,Math.PI*2,false);
ctx.stroke();
ctx.fill();
}
$("#moveLeft").click(function(){move("left");});
$("#moveRight").click(function(){move("right");});
$("#moveUp").click(function(){move("up");});
$("#moveDown").click(function(){move("down");});
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="game" width=250 height=150></canvas><br>
<canvas id="canvas" width=500 height=300></canvas><br>
<button id="moveLeft">Left</button>
<button id="moveRight">Right</button>
<button id="moveUp">Up</button>
<button id="moveDown">Down</button>
</body>
</html>
Cara anda'kembali pergi tentang hal itu sekarang tampaknya benar untuk saya. Aku akan mengubah "20" batas untuk variabel meskipun, sehingga anda dapat dengan mudah mengubah batas tingkat atau seluruh permainan jika anda pernah membutuhkan begitu.
Anda bisa abstrak ini ke logika yang spesifik "Viewport" metode, yang hanya akan menangani perhitungan yang diperlukan untuk menentukan di mana anda "Kamera" perlu pada peta, dan kemudian pastikan koordinat X dan Y dari karakter anda mencocokkan pusat kamera anda.
Anda juga bisa flip bahwa metode dan menentukan lokasi dari kamera anda berdasarkan posisi karakter (misalnya: (posisi.x - (desired_camera_size.lebar / 2))
) dan menarik kamera dari sana.
Ketika anda memiliki posisi kamera anda tahu, anda dapat mulai mengkhawatirkan tentang gambar ruangan itu sendiri sebagai lapisan pertama dari kanvas anda.
Ini adalah masalah sederhana dari pengaturan viewport untuk target's koordinat x dan y, sebagai Colton serikat, pada masing-masing frame. Mengubah yang tidak diperlukan, tetapi dapat digunakan seperti yang diinginkan. Formula yang bekerja untuk saya adalah:
function update() {
// Assign the viewport to follow a target for this frame
viewport.x = -target.x + canvas.width / 2;
viewport.y = -target.y + canvas.height / 2;
// Draw each entity, including the target, relative to the viewport
ctx.fillRect(
entity.x + viewport.x,
entity.y + viewport.y,
entity.size,
entity.size
);
}
Penjepitan pada peta adalah opsional langkah kedua:
function update() {
// Assign the viewport to follow a target for this frame
viewport.x = -target.x + canvas.width / 2;
viewport.y = -target.y + canvas.height / 2;
// Keep viewport in map bounds
viewport.x = clamp(viewport.x, canvas.width - map.width, 0);
viewport.y = clamp(viewport.y, canvas.height - map.height, 0);
// Draw each entity, including the target, relative to the viewport
ctx.fillRect(
entity.x + viewport.x,
entity.y + viewport.y,
entity.size,
entity.size
);
}
// Restrict n to a range between lo and hi
const clamp = (n, lo, hi) => n < lo ? lo : n > hi ? hi : n;
Berikut ini's contoh:
@gustavo carvalho's solusi fenomenal, tetapi melibatkan perhitungan luas dan kognitif overhead. @Colton's pendekatan ini adalah langkah di arah yang benar; terlalu buruk itu't diuraikan cukup dalam jawabannya. Aku mengambil idenya dan berlari dengan itu untuk membuat ini CodePen. Itu mencapai apa yang @user2337969 meminta untuk menggunakan konteks.menerjemahkan
. Keindahan adalah bahwa ini doesn't membutuhkan mengimbangi setiap peta atau pemain koordinat menggambar mereka adalah semudah menggunakan mereka x
dan y
secara langsung, yang jauh lebih mudah.
Pikirkan 2D kamera sebagai persegi panjang yang pans di dalam peta yang lebih besar. Sudut kiri atas pada (x, y)
koordinat dalam peta, dan ukurannya yang dari kanvas, yaitu kanvas.lebar
dan kanvas.ketinggian
. Itu berarti bahwa x
dapat berkisar dari 0
menjadi peta.lebar - kanvas.lebar
, dan y
dari 0
menjadi peta.tinggi - kanvas.ketinggian
(inklusif). Ini adalah min
dan max
yang kita makan ke @Colton's clamp
metode.
Untuk membuatnya bekerja namun, saya harus membalik tanda x
dan y
karena dengan context.translate
, nilai-nilai positif pergeseran kanvas ke kanan (membuat ilusi seolah-olah kamera panci ke kiri) dan negatif - ke kiri (karena jika kamera panci ke kanan).