So i have been working hard at a game using HTML5 and JavaScript. I am trying to make a space invaders styled game and as a result i have an array of enemies. I have separate functions dedicated to creating the array of enemies, drawing them to screen, moving the enemies and finally removing them.
the removal of enemies however is causing an issue. This is my logic
if an enemies health is less than or equal to 0, delete the enemy from the array and shrink the arrays length by 1. Now logic would dictate that this can be a calamity, if you start shooting and killing enemies from the start of the array, as the arrays length will be reduced and this is exactly my problem, so low and behold my code.
function hostile(x, y) {
this.speed = 1;
this.health = 100;
this.x = x;
this.y = y;
this.height = 32;
this.width = 32;
this.isDead = false;
this.direction = 0;
this.deadCount = 0;
this.firing = false;
//this.moving = true;
this.move = function () {
if (this.isDead === false && gameStart === true) {
context.clearRect(0, 0, canvas1.width, canvas1.height);
if (this.x > canvas.width - 64) {
this.y += 10;
this.direction = 0;
}
if (this.x < 0) {
this.y += 10;
}
if (this.direction === 1) {
this.x += this.speed;
} else {
this.x -= this.speed;
}
if (this.x < 0) {
this.direction = 1;
}
if (this.y > 420) {
this.x = 600;
}
}
};
this.draw = function () {
context.drawImage(sprite, 0, 480, 65, 68, this.x, this.y, 65, 65);
};
this.reset = function () {
context.clearRect(this.x, this.y, 65, 65);
this.x = 20;
this.y = 20;
this.health = 100;
};
};
var enemylist = [];
function createEnemies() {
for (var i = 0; i < 6; i++) {
enemylist.push(new hostile(75 * i, 20));
}
};
function deleteEnemy(a) {
enemylist.splice(a);
enemyBulletList.splice(a);
//enemylist.length = enemylist.length-1;
//enemyBulletList.length = enemyBulletList.length - 1;
};
createEnemies();
function moveEnemies() {
for (var i = 0; i < enemylist.length; i++) {
if (enemylist[i].isDead === false && gameStart === true) {
if (enemylist[i].x > canvas.width - 64) {
enemylist[i].y += 10;
enemylist[i].direction = 0;
}
if (enemylist[i].x < 0) {
enemylist[i].y += 10;
}
if (enemylist[i].direction === 1) {
enemylist[i].x += enemylist[i].speed;
} else {
enemylist[i].x -= enemylist[i].speed;
}
if (enemylist[i].x < 0) {
enemylist[i].direction = 1;
}
if (enemylist[i].y > 420) {
enemylist[i].x = 600;
}
}
}
};
So to explain my problem, i can shoot and kill enemies from the array, this will also remove them from the screen. However if i shoot an enemy from the start of the array i cause all the enemies to cleared off the screen and the game to crash. If you need more information, feel free to ask.
By request, i have submitted more code relating to my question. The code above includes the hostile function and other functions associated directly with it.
EDIT: Vitim.us had offered a very useful tip regarding my question, he suggested to create a flag of some sort (var dead = false/true etc.) and once the value is changed the specific instance can simply be removed from the screen away from the player.
Don't manually update the array like that. delete is for removing named properties. If you want to remove elements from an Array, use splice:
function deleteEnemy(a) { // capitalized functions are usually reserved for constructors
enemylist.splice(a);
enemyBulletList.splice(a);
}
Also, var enemylist = [6] doesn't do what you think it does. It creates an array with a single element, [6]. You should create an empty array and then enemylist.push(new hostile(...)) to add them to the array.
This will avoid you having to manually set the length (which you should not ever have to do.)
You can implement this in various ways
You can implement a method to hostile to remove itself from screen,
To this each instance should keep a reference to itself on screen, it can be a direct reference to DOM or it can be a property like hostile.entitieId that correlate to the onscreen object.
When hostile.health<=0 you flag hostile.isDead = true; and automatically call a method to remove it from screen, than notify other method to finally delete the instance from your enemyList[]
You can either check this on the game tick or you can build it in a event dispatch fashion, using getter and setter for the health property.
this.setHeath = function(value){
//set entity heath
if(health<=0){
//remove itself from screen
//notify other method to clear this instance from the enemyArray[]
}
}
Related
I'm still pretty new to this, so I don't know how to create a collider. My end goal is to have a game like the chrome dinosaur game. Same principles, and all. My question is, though, how do I even make a collider. I will be using a .gif for the "dinosaur". I'd like to make it where if this collider were to touch another collider, the game stops and a "game over" is shown. I have tried to create a collider, but they just keep showing up underneath the screen where the game is shown. Ant tips, tricks, or advice? Thanks
Code is as follows:
let img; //background
var bgImg; //also the background
var x1 = 0;
var x2;
var scrollSpeed = 4; //how fast background is
let music; //for music
let catBus; //catbus
//collider variables
let tinyToto;
let tiniestToto;
let hin;
let totoWithBag;
let noFace;
let happySoot;
var mode; //determines whether the game has started
let gravity = 0.2; //jumping forces
let velocity = 0.1;
let upForce = 7;
let startY = 730; //where cat bus jumps from
let startX = 70;
let totoX = 900;
let totoY = 70;
let tinToX = 900;
let tinToY = 70;
var font1; //custom fonts
var font2;
p5.disableFriendlyErrors = true; //avoids errors
function preload() {
bgImg = loadImage("backgwound.png"); //importing background
music = loadSound("catbus theme song.mp3"); //importing music
font1 = loadFont("Big Font.TTF");
font2 = loadFont("Smaller Font.ttf");
//tinyToto.setCollider("rectangle",0,25,75,75)
}
function setup() {
createCanvas(1000, 1000); //canvas size
img = loadImage("backgwound.png"); //background in
x2 = width;
music.loop(); //loops the music
catBus = {
//coordinates for catbus
x: startX,
y: startY,
};
/*
tinyToto = {
x: totoX,
y: totoY,
}
tinTo = {
x : tinToX,
y: tinToY,
}
*/
catGif = createImg("catgif.gif"); //creates catbus
catGif.position(catBus.x, catBus.y); //creates position
catGif.size(270, 100); //creates how big
/*
tinyToto = createImg("TinyToto.gif")
tinyToto.position(tinyToto.x, tinyToto.y)
tinyToto.size(270,100)
tiniestTo = createImg("tiniest Toto.gif")
tiniestTo.position(tinToX.x, tinToY.y)
tiniestTo.size(270,100)
*/
mode = 0; //game start
textSize(50); //text size
}
function draw() {
let time = frameCount; //start background loop
image(img, 0 - time, 0);
image(bgImg, x1, 2, width, height);
image(bgImg, x2, 2, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 <= -width) {
x1 = width;
}
if (x2 <= -width) {
x2 = width;
} //end background loop
fill(128 + sin(frameCount * 0.05) * 128); //text colour
if (mode == 0) {
textSize(20);
textFont(font1);
text("press SPACE to start the game!", 240, 500); //what text to type
}
fill("white");
if (mode == 0) {
textSize(35);
textFont(font2);
text("CATBUS BIZZARE ADVENTURE", 90, 450); //what text to type
}
catBus.y = catBus.y + velocity; //code for jumping
velocity = velocity + gravity;
if (catBus.y > startY) {
velocity = 0;
catBus.y = startY;
}
catGif.position(catBus.x, catBus.y);
//setCollider("tinyToto")
}
function keyPressed() {
if (keyCode === 32 && velocity == 0) {
//spacebar code
mode = 1;
velocity += -upForce;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
well, this is how I would generally do that kind of thingy:
function draw(){
for(let i in objects) // objects would be cactuses or birds
if(objects[i].x > player.x &&
objects[i].x < player.x + player.width &&
objects[i].y > player.y &&
objects[i].y < player.y + player.height){
noLoop()
// maybe do something else here
} // you could also use: for(let object of objects)
}
or if you want to do class stuff:
let player = new Player()
class Entity {
hasCollided_pointRect(_x, _y, _width, _height){
if(this.x > _x &&
this.x < _x + _width &&
this.y > _y &&
this.y < _y + _height){
return true
}
}
}
class Cactus extends Entity {
update(){
if(hasCollided_pointRect(player.x, player.y, player.width, player.height))
lossEvent()
}
}
class Player {
// ...
}
function lossEvent(){
noLoop()
}
this is a pretty classy way to do it and for a small game you really don't need all of this
also MDN has a nice article on rect with rect & point with rect collisions,
point with point collision is just (x == x && y == y)
https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
this is one of my recent loss "functions":
if(flag.health <= 0){
noLoop()
newSplashText("You lost!\nPress F5 to restart!", "center", "center", 1)
}
The way I handled game states in my Processing games was by making seperate classes for them. Then my main sketch's draw function looked something like
fun draw()
{
currentState.draw();
}
Each gamestate then acted as their own sketches (for example a menu screen, playing, game over, etc), and had a reference to the main sketch which created the states. They would then alter the main's currentState to, i.e., a new GameOverState() etc. where needed.
For now, don't worry about doing that too much if all you want a really simple gameoverscreen with an image and some text.
I would suggest a structure like this instead. Use this pseudocode in your main draw function:
fun draw()
{
if (gameOver)
{
// show game over screen
img(gameOver);
text("game over!");
// skip rest of the function
return;
}
// normal game code goes here
foo();
bar();
// update game over after this frame's game code completes
gameOver = checkGameOver();
}
Now you need a way of checking for a collision to determine the result of checkGameOver()
For the collision handling, check out Jeffrey Thompson's book/website on collision handling. It's an amazing resource, I highly recommend you check it out.
From the website I just linked, here's an excerpt from the website talking about handling collisions between 2d rectangles.
And here's a modified version of the collision handling function listed there (I updated the variable names to be a little more intuitive)
boolean rectRect(float rect1X, float rect1Y, float rect1Width, float rect1Height, float rect2X, float rect2Y, float rect2Width, float r2h)
{
// are the sides of one rectangle touching the other?
if (rect1X + rect1Width >= rect2X && // r1 right edge past r2 left
rect1X <= rect2X + rect2Width && // r1 left edge past r2 right
rect1Y + rect1Height >= rect2Y && // r1 top edge past r2 bottom
rect1Y <= rect2Y + r2h)
{ // r1 bottom edge past r2 top
return true;
}
return false;
You can use that function in your checkGameOver() function which would return a bool depending on whether your collision criteria are met.
For your game, you would loop over every obstacle in your game and check whether the dino and the obstacle overlap.
Pseudocode:
boolean checkGameOver()
{
foreach (Obstacle obstacle in obstacles)
{
if (rectRect(dino, obstacle))
{
return true;
}
}
return false;
}
I'm making a tower defense game using JavaScript and p5.js library. My enemy follows a path and their location is always stored in a list. I have a base and a gun, the gun rotates around the base(as 1 unit) and is supposed to point towards the nearest enemy. I have a function that will allow me to make the gun point towards the enemy pointEnemy however, I'm not able to get the correct condition to make it point towards the nearest enemy in it's range. I need the correct argument for enemyx & enemyy. I'm currently spawning 100 enemies and they keep moving, their location is stored in globalenemy1position. Any help is appreciated, thank you.
Required Code
Some important variables
var numberOfEnemy1 = 100
let classenemy1 = new Enemy1(numberOfEnemy1);
var globalenemy1position = [];
var isFireTowerPressed = false;
var FireTowerPos = []; // Position of all FireTowers => [x,y]
var FireTowerRange = 300;
var FireTowerAngle = 0;
My Enemy Class
class Enemy1
{
constructor(number_of_enemies)
{
this.number_of_enemies = number_of_enemies;
this.enemy_position = [];
this.enemy1speed = 4;
}
enemy1_spawn()
{
let randomx = random(-300, -100);
for(var i=0; i<this.number_of_enemies; i++)
{
var positionx = randomx;
var positiony = 100;
this.enemy_position.push([positionx + (-i*50), positiony]);
globalenemy1position.push([positionx + (-i*50), positiony]);
image(enemy1, this.enemy_position[i][0], this.enemy_position[i][1]);
}
}
enemy1_move()
{
for(var i = 0; i < this.enemy_position.length; i++)
{
image(enemy1, this.enemy_position[i][0], this.enemy_position[i][1]);
if (this.enemy_position[i][0] >= 200 && this.enemy_position[i][1] <= 450 && this.enemy_position[i][0] < 599)
{
this.enemy_position[i][1] += this.enemy1speed;
globalenemy1position[i][1] += this.enemy1speed;
}
else if (this.enemy_position[i][1] >= 100 && this.enemy_position[i][0] >= 600)
{
this.enemy_position[i][1] -= this.enemy1speed;
globalenemy1position[i][1] -= this.enemy1speed;
}
else if (this.enemy_position[i][0] >= 750)
{
this.enemy_position[i][0] = 750;
lives --;
this.enemy_position.shift();
globalenemy1position.shift();
}
else
{
this.enemy_position[i][0] += this.enemy1speed;
globalenemy1position[i][0] += this.enemy1speed;
}
}
}
}
Draw Function - Redraws Every Frame
function draw()
{
background(60, 238, 161);
[...]
classenemy1.enemy1_move();
rect(750, 70, 50, 100);
ShowLives();
if (isFireTowerPressed == true)
{
image(firetowerbaseImg, mouseX - 28, mouseY - 28);
noFill();
stroke(0,0,0);
strokeWeight(1);
circle(mouseX, mouseY, 300);
}
for (var i = 0; i < FireTowerPos.length; i++)
{
image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
if (globalenemy1position.length >= 1)
{
var gunx = FireTowerPos[i][0] +28;
var guny = FireTowerPos[i][1]+25;
var gunrange = FireTowerPos[i][3];
for (j=0; j<globalenemy1position.length; j++)
{
// Need help with this statement here
pointEnemy(globalenemy1position[j][0], globalenemy1position[j][1], gunx, guny, FireTowerPos[i][2], FireTowerPos[i][3]);
}
}
else
{
image(firetowerturretImg, FireTowerPos[i][0], FireTowerPos[i][1]-20);
}
}
}
Function to make the gun point towards Enemy - I need the proper value for enemyx & enemyy
function pointEnemy(enemyx, enemyy, gunx, guny, gunangle, gunrange)
{
const isWithinRange = dist(enemyx, enemyy, gunx, guny) < gunrange;
if(isWithinRange)
{
gunangle = atan2(enemyy - guny, enemyx - gunx) + radians(90);
}
push();
translate(gunx, guny);
// rect(-25, -20, 50, 40) // Draw the gun base
// ellipse(0, 0, gun.range*2) // display the gun range
rotate(gunangle);
image(firetowerturretImg, -28, -45); // Set the offset of the gun sprite and draw the gun
pop();
}
Here is a picture to help visualise the problem
As you can see, currently I'm just iterating through all the enemies and giving their location, so it's basically pointing to every enemy nearby.
Updates
1
I tried the approach given by #user3386109 , but wasn't able to implement it, also if possible I want the turret/gun to point towards the enemy till it leaves the range and not always point towards the closest enemy. It should start off with the closest and then keep pointing towards it till it leaves or the enemy dies(position removed from the list), whichever comes first. The function should then restart again and continue the process.
This process is the complete aiming for the tower. Add this to draw and it searches for enemies.
for (var i = 0; i < FireTowerPos.length; i++)
{
// image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
// pointEnemy(mouseX, mouseY, FireTowerPos[i][0] +28, FireTowerPos[i][1]+25, FireTowerPos[i][2], FireTowerPos[i][3]);
image(firetowerbaseImg, FireTowerPos[i][0], FireTowerPos[i][1]);
var enemiesInRange = [];
let firetowerx = FireTowerPos[i][0];
let firetowery = FireTowerPos[i][1];
for (var j = 0; j < globalenemy1position.length; j++)
{
var checkDist = dist(globalenemy1position[j][0], globalenemy1position[j][1], firetowerx, firetowery);
let thisenemyx = globalenemy1position[j][0];
let thisenemyy = globalenemy1position[j][1];
if (checkDist < FireTowerRange)
{
enemiesInRange.push([thisenemyx, thisenemyy]);
pointEnemy(enemiesInRange[0][0], enemiesInRange[0][1], FireTowerPos[i][0] +28, FireTowerPos[i][1]+25, FireTowerPos[i][2], FireTowerPos[i][3]);
}
else
{
enemiesInRange.shift();
}
}
}
I wasn't sure how to word this, because I don't actually know what exactly causes this bug. I'm trying to put together a simple Asteroids knockoff.
When the player shoots, a new object (Bullet) is created using array.push(...). Once this bullet goes beyond the canvas (out of bounds), it is deleted using array.splice(...);
The problem is that the bullets are moving in unpredictable ways. I don't know how to word it so here's the full code (working, including html/css): https://pastebin.com/tKiSnDzX
Hold spacebar for a few seconds (to shoot) and you'll see the issue clearly. You can also use A/D to turn and W to go forward.
Here's what I think is happening. The code runs fine as long as there is only one bullet on the screen (in the array). This means that either the incorrect element is being deleted or the values that go into the constructor of the object are messed up somewhere along the way.
Exhibit A (bullet constructor and its methods):
function Bullet(x,y,rot,vel) {
this.x = x;
this.y = y;
this.rot = rot;
this.vel = (vel+5);
this.move = function() {
this.x += this.vel*Math.cos(this.rot-Math.PI/2);
this.y += this.vel*Math.sin(this.rot-Math.PI/2);
}
this.draw = function() {
engine.circle(this.x, this.y, 4, "black");
var c = engine.canvas.getContext('2d');
c.translate(this.x, this.y);
c.rotate(this.rot);
c.beginPath();
c.strokeStyle="#00FF00";
c.strokeRect(-5, -5, 10, 10);
c.closePath();
c.stroke();
}
}
Exhibit B (function that creates/deletes the bullets):
shoot: function() {
if(engine.keyDown.sp == true) {
if(this.fire > 20) {
engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
this.fire = 0;
} else {
this.fire++
}
}
for(i = 0; i < engine.bullets.length; i++) {
engine.bullets[i].move();
engine.bullets[i].draw();
if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5
|| engine.bullets[i].y > engine.canvas.height+5 || engine.bullets[i].y < -5) {
console.log('bullet gone, '+i);
engine.bullets.splice(i, 1);
}
}
}
the array is declared like so: bullets: []
Thank you for any answers.
How about just tag any bullets that need to die as you come across them in your loop with something like engine.bullets[i].dead = true; Then, at the end outside the loop, filter out the dead bullets with engine.bullets = engine.bullets.filter(b => !b.dead);
You're using splice inside a for loop. When you remove element index 1, then element index 2 becomes index 1, and element index 3 becomes index 2. But your i variable is already 1, so the next iteration of the loop goes to the new index 2. But the new index 2 is the original index 3, and the original index 2 is skipped. You might be better served by a linked list:
var bulletList = null;
function bullet(){
this.next = bulletList;//if there was a list, record that
if (this.next)//and set that one to point back to this
this.next.prev = this;
bulletList = this;//place new bullet at start of list
this.previous = null;
this.draw = function(){
//do stuff here
this.next && this.next.draw();//call next item in the list, if any
if (shouldIBeRemoved){//placeholder, obviously
if (this.next && this.prev){//remove from middle of list
this.prev.next = this.next;
this.next.prev = this.prev;
}
else if (this.next && !this.prev){//remove from beginning of list
bulletList = this.next;
this.next.prev = null;
}
else if (this.prev && !this.next){//remove from end of list
this.prev.next = null;
}
else if (!this.prev && !this.next){//remove only item in list
bulletList = null;
}
}
}
}
then to draw every bullet, simply call:
if (bulletList)
bulletList.draw();
The problem was that I was translating the context in which all the bullets are, each time a new bullet was created. This meant that each bullet would be moved by x and y away from the previous one. It made it seem like the bullets were being created where they weren't supposed to be. The "unpredictability" was caused by the fact that the bullet takes on the player's rotation, so whenever a new bullet was created, it's rotation was increased by however much the player rotated before the new bullet was fired, on top of the previous bullet's rotation.
Putting context.save(); before the translation/rotation of the bullet's hitbox and context.restore(); after it perfectly solved the issue:
Bullet.draw = function() {
engine.circle(this.x, this.y, 4, "black");
if(hitbox == true) {
var c = engine.canvas.getContext('2d');
c.save();
c.translate(this.x, this.y);
//c.rotate(this.rot);
c.beginPath();
c.strokeStyle="#00FF00";
c.strokeRect(-5, -5, 10, 10);
c.closePath();
c.stroke();
c.restore();
}
}
Someone else mentioned that I was using array.splice(); in a for loop. This made it so that when a bullet (i) is deleted, the bullet right after (i+1) is moved one index back (i). So that bullet was essentially skipped over.
I could notice this sometimes when looking at the bullets while one was deleted- They "jumped" ahead.
The solution was to put i -= 1 after bullets.splice(i, 1);. This makes the next iteration of the loop go one index back, solving the occassional stuttering of the bullets:
shoot: function() {
if(engine.keyDown.sp == true) {
if(this.fire > 5) {
engine.bullets.unshift(new Bullet(this.x, this.y, this.rot, this.velocity));
this.fire = 0;
}
}
if(this.fire<=5) {
this.fire++
}
for(i = 0; i < engine.bullets.length; i++) {
engine.bullets[i].move();
engine.bullets[i].draw();
if(engine.bullets[i].x > engine.canvas.width+5 || engine.bullets[i].x < -5
|| engine.bullets[i].y > engine.canvas.height+5 || engine.bullets[i].y < -5) {
engine.bullets.splice(i, 1);
i-=1;
}
}
}
Thanks for all the answers.
Bit of a generic question but non the less I am in a situation where I do not know what to do and google has failed me!
I am trying to re-write a grid array collision with canvas that I built.
Now there is a grid object and a block object. The grid cellSize is dependent on being the same size of the block size and vice versa. The reason being is that to work out the grid array to store the blocks into I must first work out how to build it and that is dependent on the size of the block. Example,
var grid = new grid();
function grid() {
this.cellSize = 50;
this.cellsX = canvas.width / this.cellSize;
this.cellsY = canvas.height / this.cellSize;
this.buildGrid = function() {
var arr = new Array(this.cellsX);
for(var i = 0; i < arr.length; ++i) {
arr[i] = new Array(this.cellsY);
}
return arr;
};
this.arr = this.buildGrid();
this.getGridCoords = function(i) {
return Math.floor(i / this.cellSize);
};
}
function block() {
this.size = grid.cellSize; // size of the block is same as cellSize
this.x = 0;
this.y = 0 - (this.size * 1.5);
this.baseVelocity = 1;
this.velocity = this.baseVelocity;
this.update = function() {
this.y += this.velocity;
};
}
Doing it the way I have done it couples the two objects together and from what I have percieved that is a bad thing. How can I make sure that the two variables are the same size without coupling the objects if that makes sense?
The real issue is that your block() function is taking a value directly from an instance of grid().
If you want your block() function to be reusable and decoupled, its as easy as changing block() to take the size during construction.
arr[i] = new block({size: this.cellSize});
I'm coding a tile based game in javascript using canvas and was wondering how I could create a simple event handler for when the mouse enters the dimensions of a tile.
I've used jquery's http://api.jquery.com/mousemove/ in the past but for a very simple application but can't seem to wrap my head around how I'll do it in this case (quickly).
Hmm..
I started writing this post without a clue of how to do it, but I just tried using the jquery mousemove like I started above. I have a working version, but it seems 'slow' and very clunky. It's doesn't seem smooth or accurate.
I put all mode code into a js fiddle to share easily:
http://jsfiddle.net/Robodude/6bS6r/1/
so what's happening is:
1) jquery's mousemove event handler fires
2) Sends the mouse object info to the GameBoard
3) Sends the mouse object info to the Map
4) Loops through all the tiles and sends each one the mouse object
5) the individual tile then determines if the mouse coords are within its boundaries. (and does something - in this case, I just change the tiles properties to white)
but here are the sections I'm most concerned about.
$("#canvas").mousemove(function (e) {
mouse.X = e.pageX;
mouse.Y = e.pageY;
game.MouseMove(mouse);
Draw();
});
function GameBoard() {
this.Map = new Map();
this.Units = new Units();
this.MouseMove = function (Mouse) {
this.Map.MouseMove(Mouse);
};
}
function Map() {
this.LevelData = Level_1(); // array
this.Level = [];
this.BuildLevel = function () {
var t = new Tile();
for (var i = 0; i < this.LevelData.length; i++) {
this.Level.push([]);
for (var a = 0; a < this.LevelData[i].length; a++) {
var terrain;
if (this.LevelData[i][a] == "w") {
terrain = new Water({ X: a * t.Width, Y: i * t.Height });
}
else if (this.LevelData[i][a] == "g") {
terrain = new Grass({ X: a * t.Width, Y: i * t.Height });
}
this.Level[i].push(terrain);
}
}
};
this.Draw = function () {
for (var i = 0; i < this.Level.length; i++) {
for (var a = 0; a < this.Level[i].length; a++) {
this.Level[i][a].Draw();
}
}
};
this.MouseMove = function (Mouse) {
for (var i = 0; i < this.Level.length; i++) {
for (var a = 0; a < this.Level[i].length; a++) {
this.Level[i][a].MouseMove(Mouse);
}
}
};
this.BuildLevel();
}
function Tile(obj) {
//defaults
var X = 0;
var Y = 0;
var Height = 40;
var Width = 40;
var Image = "Placeholder.png";
var Red = 0;
var Green = 0;
var Blue = 0;
var Opacity = 1;
// ...
this.Draw = function () {
ctx.fillStyle = "rgba(" + this.Red + "," + this.Green + "," + this.Blue + "," + this.Opacity + ")";
ctx.fillRect(this.X, this.Y, this.Width, this.Height);
};
this.MouseMove = function (Mouse) {
if ((Mouse.X >= this.X) && (Mouse.X <= this.Xmax) && (Mouse.Y >= this.Y) && (Mouse.Y <= this.Ymax)) {
this.Red = 255;
this.Green = 255;
this.Blue = 255;
}
};
}
If you have a grid of tiles, then given a mouse position, you can retrieve the X and Y index of the tile by dividing the X mouse position by the width of a tile and Y position with the height and flooring both.
That would make Map's MouseMove:
this.MouseMove = function (Mouse) {
var t = new Tile();
var tileX = Math.floor(mouse.X / t.Width);
var tileY = Math.floor(mouse.Y / t.Height);
this.Level[tileY][tileX].MouseMove(Mouse);
};
Edit: You asked for some general suggestions. Here you go:
It's more common to use initial uppercase letters for only classes in JavaScript.
Mouse is a simple structure; I don't think it needs to have its own class. Perhaps use object literals. (like {x: 1, y: 2})
You may want to use JavaScript's prototype objects, rather than using this.method = function() { ... } for every method. This may increase performance, since it only has to create the functions once, and not whenever a new object of that class is made.