Mengelola peta teks dalam larik 2D untuk dilukis di atas kanvas HTML5


46

Jadi, saya membuat HTML5 RPG hanya untuk bersenang-senang. Peta ini berukuran <canvas>(lebar 512px, tinggi 352px | 16 ubin, 11 ubin dari atas ke bawah). Saya ingin tahu apakah ada cara yang lebih efisien untuk melukis <canvas>.

Begini cara saya memilikinya sekarang.

Bagaimana ubin dimuat dan dicat di peta

Peta sedang dilukis oleh ubin (32x32) menggunakan Image()potongan. File gambar dimuat melalui forloop sederhana dan dimasukkan ke dalam array yang dipanggil tiles[]untuk DILARANG menggunakan drawImage().

Pertama, kami memuat ubin ...

masukkan deskripsi gambar di sini

dan inilah cara ini dilakukan:

// SET UP THE & DRAW THE MAP TILES
tiles = [];
var loadedImagesCount = 0;
for (x = 0; x <= NUM_OF_TILES; x++) {
  var imageObj = new Image(); // new instance for each image
  imageObj.src = "js/tiles/t" + x + ".png";
  imageObj.onload = function () {
    console.log("Added tile ... " + loadedImagesCount);
    loadedImagesCount++;
    if (loadedImagesCount == NUM_OF_TILES) {
      // Onces all tiles are loaded ...
      // We paint the map
      for (y = 0; y <= 15; y++) {
        for (x = 0; x <= 10; x++) {
          theX = x * 32;
          theY = y * 32;
          context.drawImage(tiles[5], theY, theX, 32, 32);
        }
      }
    }
  };
  tiles.push(imageObj);
}

Tentu saja, ketika seorang pemain memulai permainan, ia memuat peta yang terakhir mereka tinggalkan. Tapi untuk di sini, itu peta semua rumput.

Saat ini, peta menggunakan array 2D. Ini contoh peta.

[[4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 1, 1, 1, 1], 
[1, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 1, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 1, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 1, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 1, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 1, 1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 13, 13, 13, 1, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 13, 13, 11, 11, 11, 13, 13, 13, 13, 13, 13, 13, 1], 
[13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 13, 13, 13, 13, 13, 1], 
[1, 1, 1, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1]];

Saya mendapatkan peta yang berbeda menggunakan ifstruktur sederhana . Setelah array 2d di atas return, nomor yang sesuai di setiap array akan dicat sesuai dengan yang Image()tersimpan di dalamnya tile[]. Kemudian drawImage()akan muncul dan melukis sesuai dengan xdan ydan waktu itu dengan 32melukis pada x-ykoordinat yang benar .

Bagaimana banyak perpindahan peta terjadi

Dengan permainan saya, peta memiliki lima hal yang dapat melacak: currentID, leftID, rightID, upID, dan bottomID.

  • currentID: ID saat ini dari peta tempat Anda berada.
  • leftID: ID currentIDuntuk memuat apa saat Anda keluar di sebelah kiri peta saat ini.
  • rightID: ID currentIDuntuk memuat apa saat Anda keluar di kanan peta saat ini.
  • downID: ID currentIDuntuk memuat apa saat Anda keluar di bagian bawah peta saat ini.
  • upID: ID currentIDuntuk memuat apa saat Anda keluar di bagian atas peta saat ini.

Sesuatu untuk catatan: Jika salah leftID, rightID, upID, atau bottomIDTIDAK tertentu, yang berarti mereka adalah 0. Itu berarti mereka tidak dapat meninggalkan sisi peta itu. Ini hanyalah blokade yang tidak terlihat.

Jadi, begitu seseorang keluar dari sisi peta, tergantung di mana mereka keluar ... misalnya jika mereka keluar di bagian bawah, bottomIDakankah jumlah mapmemuatnya dan dengan demikian akan dilukis di peta.

Berikut ini .GIF representasional untuk membantu Anda memvisualisasikan dengan lebih baik:

masukkan deskripsi gambar di sini

Seperti yang Anda lihat, cepat atau lambat, dengan banyak peta saya akan berurusan dengan banyak ID. Dan itu mungkin bisa sedikit membingungkan dan sibuk.

Kelebihan yang jelas adalah bahwa ia memuat 176 ubin sekaligus, menyegarkan kanvas 512x352 kecil, dan menangani satu peta sekaligus. Yang menipu adalah id MAP, ketika berhadapan dengan banyak peta, terkadang membingungkan.

Pertanyaan saya

  • Apakah ini cara yang efisien untuk menyimpan peta (mengingat penggunaan ubin), atau apakah ada cara yang lebih baik untuk menangani peta?

Saya berpikir sepanjang garis peta raksasa. Ukuran peta besar dan semuanya adalah satu array 2D. Viewport, bagaimanapun, masih 512x352 piksel.

Berikut ini .gif yang saya buat (untuk pertanyaan ini) untuk membantu memvisualisasikan:

masukkan deskripsi gambar di sini

Maaf jika Anda tidak dapat mengerti bahasa Inggris saya. Silakan tanyakan apa pun yang Anda punya kesulitan untuk mengerti. Semoga, saya membuatnya jelas. Terima kasih.


16
Upaya menjawab pertanyaan ini dengan grafik dan semua layak mendapat dukungan. :)
Tapio

Jawaban:


5

Sunting: Baru saja melihat bahwa jawaban saya didasarkan pada kode Anda tetapi tidak benar-benar menjawab pertanyaan Anda. Saya menyimpan jawaban lama jika Anda dapat menggunakan informasi itu.

Sunting 2: Saya telah memperbaiki 2 masalah dengan kode asli: - Tambahan +1 dalam perhitungan akhir x dan y adalah kesalahan di dalam tanda kurung, tetapi perlu ditambahkan setelah pembagian. - Saya lupa memvalidasi nilai x dan y.

Anda bisa saja memiliki peta saat ini di memori dan memuat peta sekitarnya. Bayangkan sebuah dunia yang terdiri dari array 2d tingkat tunggal ukuran 5x5. Pemain mulai di bidang 1. Karena batas atas dan kiri level ada di ujung dunia, mereka tidak perlu dimuat. Jadi dalam hal ini level 1/1 aktif dan level 1/2 dan 2/1 dimuat ... Jika pemain sekarang bergerak ke kanan, semua level (selain yang Anda pindahkan) diturunkan dan lingkungan baru sarat. Berarti level 2/1 sekarang aktif, 1/1, 2/2 dan 3/1 sekarang dimuat.

Saya harap ini memberi Anda gambaran bagaimana hal itu bisa dilakukan. Tetapi pendekatan ini tidak akan bekerja dengan sangat baik, ketika setiap level harus disimulasikan selama pertandingan. Tetapi jika Anda dapat membekukan level yang tidak digunakan ini harus bekerja dengan baik.

Jawaban lama:

Apa yang saya lakukan saat merender level (juga berbasis ubin) adalah menghitung item mana dari array ubin yang bersinggungan dengan viewport dan kemudian saya hanya membuat ubin itu. Seperti itu Anda dapat memiliki peta besar tetapi hanya perlu membuat bagian di layar.

//Mock values
//camera position x
//viewport.x = 233f;
//camera position y
//viewport.y = 100f;
//viewport.w = 640
//viewport.h = 480
//levelWidth = 10
//levelHeight = 15
//tilesize = 32;

//startX defines the starting index for the x axis.
// 7 = 233 / 32
int startX = viewport.x / tilesize;

//startY defines the starting index
// 3 = 100 / 32
int startY = viewport.y / tilesize;

//End index y
// 28 = (233 + 640) / 32 + 1
int endX = ((viewport.x + viewport.w) / tilesize) + 1;

//End index y
// 19 = (100 + 480) / 32 + 1
int endX = ((viewport.x + viewport.w) / tilesize) + 1;

//Validation
if(startX < 0) startX = 0;
if(startY < 0) startY = 0;
//endX is set to 9 here
if(endX >= levelWidth) endX = levelWidth - 1;
//endX is set to 14 here
if(endY >= levelHeight) endY = levelHeight - 1;

for(int y = startY; y < yEnd; y++)
    for(int x = startX; x < xEnd; x++)
          [...]

Catatan: Kode di atas tidak diuji tetapi harus memberikan ide apa yang harus dilakukan.

Mengikuti contoh dasar untuk representasi viewport:

public class Viewport{
    public float x;
    public float y;
    public float w;
    public float h;
}

Representasi javascript akan terlihat seperti

<script type="text/javascript">
//Declaration
//Constructor
var Viewport = function(xVal, yVal, wVal, hVal){
    this.x = xVal;
    this.y = yVal;
    this.w = wVal;
    this.h = hVal;
}
//Prototypes
Viewport.prototype.x = null;
Viewport.prototype.y = null;
Viewport.prototype.w = null;
Viewport.prototype.h = null;
Viewport.prototype.toString = function(){
    return ["Position: (", this.x, "/" + this.y + "), Size: (", this.w, "/", this.h, ")"].join("");
}

//Usage
var viewport = new Viewport(23, 111, 640, 480);
//Alerts "Position: (23/111), Size: (640/480)
alert(viewport.toString());
</script>

Jika Anda mengambil contoh kode saya, Anda harus mengganti dek int dan float dengan var. Juga pastikan bahwa Anda menggunakan Math.floor pada perhitungan tersebut, yang ditugaskan ke nilai-nilai berbasis int, untuk mendapatkan perilaku yang sama.

Satu hal yang akan saya pertimbangkan (walaupun saya tidak yakin apakah ini penting dalam javascript) adalah untuk meletakkan semua ubin (atau sebanyak mungkin) dalam satu tekstur besar alih-alih menggunakan banyak file tunggal.

Berikut ini tautan yang menjelaskan cara melakukannya: http://thiscouldbebetter.wordpress.com/2012/02/25/slicing-an-image-into-tiles-in-javascript/


Hanya untuk memperjelas ... viewport.x akan ditetapkan sebagai "531" dan viewport.y akan menjadi "352" dan tilesize = "32". Dan seperti..?
tes

Jika maksud Anda posisi kamera 531 maka ya. Saya menambahkan beberapa komentar pada kode di atas.
tom van green

Saya menggunakan JavaScript ... Saya Java hampir identik dengan ini.
Tes

Ya, Anda hanya perlu membuat kelas viewport berbasis js (atau Anda bisa membuat 4 variabel x, y, w dan h). Anda juga harus memastikan ke lantai (Math.floor) perhitungan tersebut, yang ditugaskan untuk nilai int. Saya menambahkan contoh kode, bagaimana tampilan kelas viewport. Pastikan saja, bahwa Anda meletakkan deklarasi sebelum penggunaan.
tom van green

Hanya mengatakan bahwa Anda memasukkan 640, 480.. tetapi dapat bekerja dengan 512, 352benar?
Tes

3

Menyimpan peta sebagai ubin berguna untuk memungkinkan Anda menggunakan kembali aset dan mendesain ulang peta. Meskipun secara realistis itu tidak benar-benar menawarkan banyak manfaat bagi pemain. Plus, Anda kemudian menggambar ulang setiap ubin setiap kali. Inilah yang akan saya lakukan:

Simpan seluruh peta sebagai satu array besar. Tidak ada gunanya memiliki peta dan submaps, serta berpotensi sub-peta (jika Anda memasuki bangunan, misalnya). Anda memuat semuanya dan ini membuat semuanya bagus dan sederhana.

Jadikan peta sekaligus. Buat kanvas di luar layar tempat Anda merender peta dan gunakan ini di gim Anda. Halaman ini menunjukkan kepada Anda bagaimana melakukannya dengan fungsi javascript sederhana:

var renderToCanvas = function (width, height, renderFunction) {
  var buffer = document.createElement('canvas');
  buffer.width = width;
  buffer.height = height;
  renderFunction(buffer.getContext('2d'));
  return buffer;
};

Ini dengan asumsi peta Anda tidak akan banyak berubah. Jika Anda ingin membuat sub-peta di tempat (misalnya, bagian dalam bangunan), Anda hanya merendernya ke kanvas juga lalu menggambarnya di atas. Berikut ini contoh bagaimana Anda dapat menggunakan ini untuk membuat peta Anda:

var map = renderToCanvas( map_width*32, map_height*32, renderMap );

var MapSizeX = 512;
var MapSizeY = 352;

var draw = function(canvas, mapCentreX, mapCentreY) {
  var context = canvas.getContext('2d');

  var minX = playerX - MapSizeX/2;
  var minY = playerY - MapSizeY/2;

  context.drawImage(map,minX,minY,MapSizeX,MapSizeY,0,0,MapSizeX,MapSizeY);
}

Ini akan menggambar sebagian kecil dari peta yang berpusat di sekitar mapCentreX, mapCentreY. Ini akan menawarkan Anda bergulir mulus di sekitar seluruh peta, serta gerakan sub-ubin - Anda dapat menyimpan posisi pemain sebagai 1/32 dari ubin, sehingga Anda bisa mendapatkan pergerakan yang mulus di peta (Jelas jika Anda ingin pindahkan ubin-demi-ubin sebagai pilihan gaya, Anda bisa menambahkan dalam potongan 32).

Anda masih dapat membuat peta Anda jika Anda suka, atau jika dunia Anda sangat besar dan tidak akan muat dalam memori pada sistem target Anda (ingat bahwa bahkan untuk 1 byte per piksel, peta 512x352 adalah 176 KB) tetapi dengan pra -menampilkan peta Anda seperti ini Anda akan melihat peningkatan kinerja besar di sebagian besar browser.

Apa yang Anda dapatkan adalah fleksibilitas menggunakan kembali ubin di seluruh dunia tanpa harus pusing menyunting satu gambar besar, tetapi juga memungkinkan Anda menjalankan ini dengan cepat dan mudah dan hanya mempertimbangkan satu 'peta' (atau sebanyak 'peta' yang Anda inginkan) .



1

Cara mudah untuk melacak ID adalah dengan menggunakan array 2d lainnya di dalamnya: dengan hanya mempertahankan x, y pada "array meta" itu, Anda dapat dengan mudah mencari ID lain.

Yang sedang berkata, inilah Google mengambil game kanvas ubin 2D (well, sebenarnya HTML5 multiplayer, tetapi mesin ubin juga dibahas) dari I / O 2012 terbaru mereka: http://www.youtube.com/watch?v=Prkyd5n0P7k Ini juga memiliki kode sumber yang tersedia.


1

Ide Anda untuk memuat seluruh peta menjadi sebuah array terlebih dahulu, adalah cara yang efisien, tetapi akan mudah untuk mendapatkan JavaScript Injeksi, siapa pun akan dapat mengetahui peta dan mulai petualangan.

Saya sedang mengerjakan game RPG berbasis web juga, cara saya memuat peta saya adalah melalui Ajax dari halaman PHP, yang memuat peta dari basis data, di mana dikatakan X, Y, posisi Z dan vlaue dari ubin, menggambarnya, dan akhirnya pemain bergerak.

Saya masih mengerjakannya untuk membuatnya lebih efisien, tetapi peta memuat cukup cepat hingga tetap seperti ini sekarang.


1
Bisakah saya melihat demo?
tes

1

Tidak , array adalah cara paling efisien untuk menyimpan peta ubin .

Mungkin ada beberapa struktur yang memerlukan sedikit memori lebih sedikit tetapi hanya dengan biaya yang jauh lebih tinggi untuk memuat dan mengakses data.


Namun yang perlu dipertimbangkan adalah kapan Anda akan memuat peta berikutnya. Mengingat bahwa peta tidak terlalu besar, hal yang sebenarnya akan memakan waktu paling lama adalah menunggu server untuk mengirimkan peta. Pemuatan aktual hanya akan memakan waktu beberapa milidetik. Tapi penantian ini bisa dilakukan secara tidak sinkron, tidak harus menghalangi alur permainan. Jadi hal terbaik adalah tidak memuat data saat dibutuhkan tetapi sebelumnya. Pemain berada dalam satu peta, sekarang memuat semua peta yang berdekatan. Pemain bergerak di peta berikutnya, memuat semua peta yang berdekatan lagi, dll., Dll. Pemain bahkan tidak akan menyadari bahwa permainan Anda dimuat di latar belakang.

Dengan cara ini Anda juga dapat membuat ilusi dunia kurang perbatasan dengan juga menggambar peta yang berdekatan juga, dalam hal ini Anda perlu memuat tidak hanya peta yang berdekatan tetapi juga yang berdekatan.


0

Saya tidak yakin mengapa Anda berpikir: Seperti yang Anda lihat, cepat atau lambat, dengan banyak peta, saya akan berurusan dengan banyak ID. Dan itu mungkin bisa sedikit membingungkan dan sibuk.

LeftID akan selalu menjadi -1 dari currentID, rightID akan selalu menjadi +1 dari currentID.

UpID akan selalu menjadi - (total peta Lebar) dari ID saat ini dan downID akan selalu menjadi + (total peta lebar) dari ID saat ini dengan pengecualian nol berarti Anda telah mencapai ujung.

Satu peta dapat dipecah menjadi banyak peta, dan disimpan secara berurutan dengan mapIDXXXX di mana XXXX adalah id. Dengan begitu, tidak ada yang membingungkan. Saya pernah berkunjung ke situs Anda dan sepertinya, saya bisa saja salah, bahwa masalahnya ada pada editor peta Anda yang memaksakan batasan ukuran yang menghambat otomasi Anda untuk memecah peta besar menjadi beberapa yang kecil di save dan batasan ini adalah mungkin membatasi solusi teknis.

Saya telah menulis editor peta Javascript yang menggulir ke segala arah, 1000 x 1000 (saya tidak akan merekomendasikan ukuran itu) ubin dan masih bergerak sebaik 50 x 50 peta dan itu dengan 3 lapisan termasuk parallax scrolling. Ini menyimpan dan membaca dalam format json. Saya akan mengirim Anda salinan jika Anda mengatakan Anda tidak keberatan dengan email sekitar 2meg. Ini dimaksudkan untuk berada di github tetapi saya belum mendapatkan tileset (legal) yang layak yang mengapa saya belum repot-repot meletakkannya di sana.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.