0
\$\begingroup\$

I am trying to remake Super Mario Bros. in JavaScript and I am trying to figure out if there is a more efficient/shorter way to create and store the level data. I have created 1-1 and here's what the code looks like:

const repeatBlock = (block, start, end, array) => { let newData = { "tp1": "tlp", "tp0": "trp", "p1": "lp", "p0": "rp" }; for (let i = start; i <= end; i++) { let oldBlock = block; if (newData.hasOwnProperty(block+(end-i))) { block = newData[block+(end-i)]; } array[i] = block; block = oldBlock; } } let level1_1Overworld = []; for (let i = 0; i <= 14; i++) { level1_1Overworld[i] = []; repeatBlock("", 0, 227, level1_1Overworld[i]); } level1_1Overworld[2][198] = "f"; level1_1Overworld[5][22] = "q c"; repeatBlock("b", 80, 87, level1_1Overworld[5]); repeatBlock("b", 91, 93, level1_1Overworld[5]); level1_1Overworld[5][94] = "q c"; level1_1Overworld[5][109] = "q p"; repeatBlock("b", 121, 123, level1_1Overworld[5]); level1_1Overworld[5][128] = "b"; repeatBlock("q c", 129, 130, level1_1Overworld[5]); level1_1Overworld[5][131] = "b"; repeatBlock("s", 188, 189, level1_1Overworld[5]); repeatBlock("s", 187, 189, level1_1Overworld[6]); repeatBlock("s", 186, 189, level1_1Overworld[7]); level1_1Overworld[8][65] = "h"; repeatBlock("", 66, 184, level1_1Overworld[8]); repeatBlock("s", 185, 189, level1_1Overworld[8]); level1_1Overworld[8][202] = "C"; level1_1Overworld[9][16] = "q c"; level1_1Overworld[9][20] = "b"; level1_1Overworld[9][21] = "q p"; level1_1Overworld[9][22] = "b"; level1_1Overworld[9][23] = "q c"; level1_1Overworld[9][24] = "b"; repeatBlock("tp", 46, 47, level1_1Overworld[9]); repeatBlock("tp", 57, 58, level1_1Overworld[9]); level1_1Overworld[9][77] = "b"; level1_1Overworld[9][78] = "q p"; level1_1Overworld[9][79] = "b"; level1_1Overworld[9][94] = "b m"; level1_1Overworld[9][100] = "b"; level1_1Overworld[9][101] = "b s"; level1_1Overworld[9][106] = "q c"; level1_1Overworld[9][109] = "q c"; level1_1Overworld[9][112] = "q c"; level1_1Overworld[9][118] = "b"; repeatBlock("b", 129, 130, level1_1Overworld[9]); level1_1Overworld[9][137] = "s"; level1_1Overworld[9][140] = "s"; repeatBlock("s", 151, 152, level1_1Overworld[9]); level1_1Overworld[9][155] = "s"; repeatBlock("b", 168, 169, level1_1Overworld[9]); level1_1Overworld[9][170] = "q c"; level1_1Overworld[9][171] = "b"; repeatBlock("", 172, 183, level1_1Overworld[9]); repeatBlock("s", 184, 189, level1_1Overworld[9]); repeatBlock("tp", 38, 39, level1_1Overworld[10]); repeatBlock("p", 46, 47, level1_1Overworld[10]); repeatBlock("p", 57, 58, level1_1Overworld[10]); repeatBlock("s", 136, 137, level1_1Overworld[10]); repeatBlock("s", 140, 141, level1_1Overworld[10]); repeatBlock("s", 150, 152, level1_1Overworld[10]); repeatBlock("s", 155, 156, level1_1Overworld[10]); repeatBlock("s", 183, 189, level1_1Overworld[10]); repeatBlock("tp", 28, 29, level1_1Overworld[11]); repeatBlock("p", 38, 39, level1_1Overworld[11]); repeatBlock("p", 46, 47, level1_1Overworld[11]); repeatBlock("p", 57, 58, level1_1Overworld[11]); repeatBlock("s", 135, 137, level1_1Overworld[11]); repeatBlock("s", 140, 142, level1_1Overworld[11]); repeatBlock("s", 149, 152, level1_1Overworld[11]); repeatBlock("s", 155, 157, level1_1Overworld[11]); repeatBlock("tp", 163, 164, level1_1Overworld[11]); repeatBlock("tp", 179, 180, level1_1Overworld[11]); repeatBlock("s", 182, 189, level1_1Overworld[11]); repeatBlock("p", 28, 29, level1_1Overworld[12]); repeatBlock("p", 38, 39, level1_1Overworld[12]); repeatBlock("p", 46, 47, level1_1Overworld[12]); repeatBlock("p", 57, 58, level1_1Overworld[12]); repeatBlock("s", 134, 137, level1_1Overworld[12]); repeatBlock("s", 140, 143, level1_1Overworld[12]); repeatBlock("s", 148, 152, level1_1Overworld[12]); repeatBlock("s", 155, 158, level1_1Overworld[12]); repeatBlock("p", 163, 164, level1_1Overworld[12]); repeatBlock("p", 179, 180, level1_1Overworld[12]); repeatBlock("s", 181, 189, level1_1Overworld[12]); level1_1Overworld[12][198] = "s"; for (let i = 13; i <= 14; i++) { repeatBlock("g", 0, 68, level1_1Overworld[i]); repeatBlock("g", 71, 85, level1_1Overworld[i]); repeatBlock("g", 89, 152, level1_1Overworld[i]); repeatBlock("g", 155, 227, level1_1Overworld[i]); } //1-1 Bonus 16x15 let level1_1Bonus = []; for (let i = 0; i <= 15; i++) { level1_1Bonus[i] = []; repeatBlock("", 0, 15, level1_1Bonus[i]); } for (let i = 2; i <= 12; i++) { level1_1Bonus[i][0] = "b"; if (i < 11) { level1_1Bonus[i][15] = "lp"; } } repeatBlock("b", 4, 10, level1_1Bonus[2]); repeatBlock("c", 5, 9, level1_1Bonus[5]); repeatBlock("c", 4, 10, level1_1Bonus[7]); repeatBlock("c", 4, 10, level1_1Bonus[9]); for (let i = 10; i <= 12; i++) { repeatBlock("b", 4, 10, level1_1Bonus[i]); } level1_1Bonus[11][13] = "spul"; level1_1Bonus[11][14] = "spum"; level1_1Bonus[11][15] = "spur"; level1_1Bonus[12][13] = "spll"; level1_1Bonus[12][14] = "splm"; level1_1Bonus[12][15] = "splr"; for (let i = 13; i <= 14; i++) { repeatBlock("g", 0, 15, level1_1Bonus[i]); } 

After the arrays I have a function that translates each index of the array to what it's corresponding type would be. This code works and I am able to render everything properly but I feel like there could be a better way to create this data especially when this is only the first level. Any ideas?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome to Code Review! The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?, as well as How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles. \$\endgroup\$ Commented Jun 16, 2023 at 4:10
  • \$\begingroup\$ One way to make it more "efficient", but (debatably) not "shorter" or "quicker", could be to use the JSON file format seeing as how your game is built using JavaScript. It would be an interesting challenge to see how you decide to convert your level from code into a JSON file. \$\endgroup\$ Commented Jun 16, 2023 at 19:02

1 Answer 1

2
\$\begingroup\$

Creating complex maps.

Your method

That is some very ugly looking code you have. Just to move an item would take a lot of work, and could potentially break the game each time you make changes.

I am way to old for Super Mario Bros (it was a kids game to me at the time) so I am not sure of what should be what in your code. I have no clue what your map looks like and what each block is (apart from "g" which I guess is ground)

Visual editor

Ideally you would write a custom map editor. That is a lot of extra work, but there are other ways to visually create 2D arrays of data.

Abstract blocks

Image editor

You can use a basic image editor, where each pixel represents a block (abstracted blocks as pixels).

For example.

  • Create an image that is the size of the map. Eg 228 by 15 px.
  • Match a set of colors to each block. Eg transparent is empty block, orange is ground, etc...
  • Load the image, scan each pixel and add the corresponding block for each color.

This is easy to do in JavaScript via the DOM's CanvasRenderingContext2D. But do note that image editors can very slightly modify pixel RGBA values, so each colour representing a block should be a range of colors.

Text editor

You can do the same with a text editor. Have a single character represent a block, and visually create the map in the editor.

If the map is too wide, make it in sections.

Example using text editor

NOTE The code below is untested and likely has typos. It is meant as an example only, to be safe you should write and test your own code.

The map is created as an array of string, each character represents a block. To match characters to blocks an object is create lookup the block by character blockTypes

A text map below mapA is part of a level map.

 //=================================================================================================================================================================================================== // Columns -> 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 // 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| | // | 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| const mapA = [// | | | | | | | | | | | | | | | | | | | /* 0 */ ` ', /* 1 */ ` ', /* 2 */ ` ', /* 3 */ ` ', /* 4 */ ` ', /* 5 */ ` C ', /* 6 */ ` ++++.++++++.++++++++ +++.+++.+++ +++++.+++++++++.++++++.++++++ ', /* 7 */ ` ???????? + ', /* 8 */ ` ++++ ???????? ', /* 9 */ ` ^ +++++++ ++ ', /* 10 */ ` ^ - ++++++++++ ^ ^ ++++ ^ ^ ', /* 11 */ ` ^ - - +++++++++++++ ^ - ^ - ++++ - ^ - ^ +++', /* 12 */ ` - C - C - +++++++++++++++ - C - C C - - ++++ - - C C - - h+++', /* 13 */ `#################################################################### ############## ################################################################ ##########################', /* 14 */ `#################################################################### ############## ################################################################ ##########################', // ^ | | | | | | | | | | | | | | | | | | | // Rows | | 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| // 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| 0123456789| | // 0 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 ]; 

An object to lookup block types..

const blockTypes = { " ": "", "#": "g", ".": "f", "^": "q c", "-": "q p", b: "b", s: "s", h: "h", c: "C", p: "p", t: "tp", "?": "b m", "+": "b s", }; 

And some functions to do the work.

First create an empty level map...

function CreateEmptyMap(width, height, block = "") { const map = []; while (height-- > 0) { const row = []; let i = width; while (i-- > 0) { row.push(block); } map.push(row) } return map; } 

And a function that adds a text map to the level map. The text map can be any size, as can the level map. The text map is added to the level map at the position left, top.

function AddMap(level, left, top, map, blocks = blockTypes) { var x, y = 0; const height = level.length; if (height) { const width = level[0].length; if (width) { for (const row of map) { if (top + y >= 0 && top + y < height) { x = 0; for (const char of row) { if (left + x >= 0 && left + x < width) { level[top + y][left +x] = blockTypes[char] ?? blockTypes[" "]; } x ++; } } y++ } } } return level; } 

Then to create the level...

const level1_1Overworld = CreateEmptyMap(228, 15); // Create empty map AddMap(level1_1Overworld, 0, 0, mapA); // add mapA to level map 
\$\endgroup\$
1
  • \$\begingroup\$ Thank you a lot for this recommendation I have started implementing it in my code and I can say that it is much more readable/better than before. \$\endgroup\$ Commented Jun 18, 2023 at 20:54

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.