I am building 2D tile game map. Each tile is a div in 2D array (var tiles = []). Below is the function which builds a tile based on some arguments defined somewhere else:
function Tile(rnd, px, py, nid) {
var self = this;
var _types = ["grass","forest","hills","swamp","forest", "mountains"];
var height = 60;
var width = 60;
var tileID = nid // new numeric tile id
var id = "tile_"+px+py+rnd; // old tile ID
var x = px;
var y = py;
var type = _types[rnd];
var img = 'img/maptiles/'+type+'.png';
this.Draw = function() {
var div = $("<div class='tile'></div>");
div.attr('id',tileID).data('type',type).data('x',x).data('y',y);
div.get(0).tile = self;
div.css({top:height*y, left:width*x});
div.css({"background":"url('"+img+"')"});
div.appendTo('#map-content');
};
this.Alert = function() {
alert("Tile type: "+type+". Tile ID: "+tileID+" ");
};
this.Move = function(){ // moves player to available tile, in this case if player stands to a tile before the clicked one
alert("start move! Tile type: "+type+". Tile ID: "+tileID+" ");
if (playerPosition === tileID - 1) {
$("#player").remove();
$("#????")").append('<img id="player" src="Pictures/maptiles/player.png" />');
playerPosition = tileID;
alert("Player position now: " + playerPosition);
}
};
}
As a result I end up with m x n map made of divs each with a unique numeric ID starting with 1. I know that using numeric IDs for elements is(was?) frowned upon, though HTML5 specs do not actually prohibit this anymore.
Now what I want to do is to place a player icon (player.png) depending on player's position. Starting position is 1 and I am using tiles (aka divs) numeric IDs to calculate legal moves (can move only to bordering tiles).
When a tile is clicked this.Move is called. Its purpose is to check if player can move on a clicked tile (just one IF statement for now to test) and must remove player.png and redraw it on the clicked div.
Here I run into a problem since I need to somehow use the tileID (which is equal to Div ID) to tell browser to append the DIV which is belong clicked (as I obviously do not write a function for every div on the field). I think that since I can get the DIV id on click I can use it somehow.
I tried to experiment with this.div.id:eq("+tileID+") but with no luck.
UPD:
Adding click handler as requested. Note this is within var Map, which is responsible for building the map, rendering and handling some user input:
var Map = new function() {
var maxHorz = 20;
var maxVert = 5;
var tiles = [];
this.init = function() {
for(var i=0; i<maxVert; i++) {
tiles[i] = [];
for(var j=0; j<maxHorz; j++) {
tiles[i][j] = new Tile(Math.random()*6|0, j, i, tileID++);
}
}
Render();
Setup();
};
this.GetMap = function() {
return tiles;
};
var Render = function() {
$.each(tiles, function(k,v){
$.each(v, function(k,t){
t.Draw();
});
});
};
var Setup = function(){
$('#map-content').on('click','div.tile', function(e){
//var tile = tiles[$(this).data('y')][$(this).data('x')];
this.tile.Move();
});
}
this.Redraw = function(x,y) {
};
}
Map.init();
The answer is found, finally :)
$('#player').detach().appendTo($('#'+tileID))
Related
I'm creating a small 2d-minecraft clone, in Phaser js, on my own as a learning experience. So far I have gotten player movement and random level seeds to work ok.
I am using Phasers P2JS engine and have sprites that are box based. What I'm struggling with now Is I want the player to be able to walk unhindered up small elevations, (1-tile high) but I don't have any good idea of how I should Implement this.
I have tried changing the bounding box of the player so that it had a slope at the bottom but this gets me in to a bunch of trouble with wall climbing. I want a way to do this where it gets as seamless as possible. Preferably the player speed is not altered much by climbing the steps.
I am concidering writing some kind of collision detection function to handle this but I am uncertain if this is the best way to do it.
Thanks for your help.
Below is my code and an image that shows the kind of step I want to beable to walk up. Its the first elevation to the left in the image.
var pablo = require('../generators/pablo.js');
var destiny = {};
var socket;
var player;
var jumpButton;
var levelCollisionGroup;
var playerCollisionGroup;
destiny.create = function () {
console.info("game loaded");
// World
this.game.world.setBounds(0, 0, 4000, 1000);
this.game.physics.startSystem(Phaser.Physics.P2JS);
this.game.physics.p2.gravity.y = 600;
this.game.physics.p2.applySpringForces= false;
this.game.physics.p2.applyDamping= false;
this.game.physics.p2.restitution = 0;
this.game.physics.p2.friction = 0.01;
// Player
playerCollisionGroup = this.game.physics.p2.createCollisionGroup();
player = this.game.add.sprite(this.game.world.centerX, 800, 'player');
this.game.physics.p2.enable(player,true);
player.body.fixedRotation = true;
player.body.setCollisionGroup(playerCollisionGroup);
player.body.mass = 2;
// Camera
this.game.camera.follow(player);
this.game.camera.deadzone = new Phaser.Rectangle(200, 0, 400, 100);
// Controls
jumpButton = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
leftButton = this.game.input.keyboard.addKey(Phaser.Keyboard.A);
rightButton = this.game.input.keyboard.addKey(Phaser.Keyboard.D);
// Level
levelCollisionGroup = this.game.physics.p2.createCollisionGroup();
this.game.physics.p2.updateBoundsCollisionGroup();
for (i = 0; i < 280; i = i + 1) {
var block;
var height = pablo.getHeight(i);
for(j = 0; j < height; j = j + 1){
if(j === height-1){
block = this.game.add.sprite(15*i, 993-15*j, 'grass');
} else {
block = this.game.add.sprite(15*i, 993-15*j, 'dirt');
}
block.width = 15;
block.height = 15;
this.game.physics.p2.enable(block);
block.body.static=true;
block.body.immovable = true;
block.body.collides([levelCollisionGroup, playerCollisionGroup]);
block.body.setCollisionGroup(levelCollisionGroup);
if(j == height){
}
}
}
player.body.collides(levelCollisionGroup);
this.game.stage.backgroundColor = "#5599CC";
};
destiny.update = function() {
player.body.velocity.x=0;
if (leftButton.isDown) {
player.body.velocity.x = -200;
} else if (rightButton.isDown) {
player.body.velocity.x = 200;
}
if (jumpButton.isDown && this.checkIfCanJump()) {
player.body.velocity.y = -400;
}
};
destiny.render = function() {
this.game.debug.cameraInfo(this.game.camera, 32, 32);
this.game.debug.spriteCoords(player, 32, 550);
};
destiny.checkIfCanJump = function() {
var result = false;
for (var i=0; i < this.game.physics.p2.world.narrowphase.contactEquations.length; i++) {
var c = this.game.physics.p2.world.narrowphase.contactEquations[i];
if (c.bodyA === player.body.data || c.bodyB === player.body.data) {
var d = p2.vec2.dot(c.normalA, p2.vec2.fromValues(0, 1));
if (c.bodyA === player.body.data) {
d *= -1;
}
if (d > 0.5) {
result = true;
}
}
}
return result;
};
module.exports = destiny;
===================== Edit =====================
I have now tried creating slopes of the edge pieces when generating the world. But I realized that this makes me have to regenerate the world when I later add the feature for hacking away blocks. Thus this is not the solution. I think I will need to do some collision detection and move the player up when I hit an edge. But I'm not quite sure how to do this in phaser. Any help is still appreciated.
!!! Here is an image of what not to do !!!
Emanuele Feronato has a post on replicating the game Magick in Phaser.
There he covers the case of a block colliding with a barrier/wall, with the ability of the block to climb one level up.
You can check the tutorial, but what he appears to be doing is checking to see if the diagonal tile is empty (in other words, is it just a 'step' up), and if it is, running a 'jump' function, which looks more like a climb.
Depending upon how you want your character to step, you could potentially look at both the next tile (on the x-axis) as well as the one after it to check for the height.
So for example, if moving right and the next tile is flat, but the second tile has a step, you might start moving your character up on the y-axis.
Hello I am new to javascript so I apologize if this is silly however, I am creating a generator that displays a .png for each letter of the alphabet. The goal is to overlap and display multiple images inside of a div, with random positions. So far I am able to display images for each letter on top of one another by creating a random z-index, however I can not figure out how to alter the positioning to random for each picture.
I attempted to create a Math.floor random variable called randomThingTwo to alter the top positioning of the images, but this did not work.
Please help!
here is my current code:
var x,y,splitted;
function generate() {
x = document.getElementById("form1");
y = x.elements["message"].value;
var text = [y];
var joined = text.join();
var res = joined.toLowerCase();
var regexp = /[A-z]/g;
splitted = res.match(regexp);
var words = [];
judge();
}
var counter = -1;
function judge() {
if (counter < y.length) {
counter++;
art();
}
}
function art() {
img = new Image(splitted[counter] + '.png');
var picture = document.getElementById("pic");
var img = document.createElement('img');
var randomThing = Math.floor((Math.random() * 10) + 1);
console.log(randomThing);
// var randomThingTwo = Math.floor((Math.random() * 20) + 1);
img.setAttribute("src", splitted[counter]+".png");
img.style.zIndex= randomThing;
img.style.position= "absolute";
// img.style.position.marginTop = randomThingTwo;
img.setAttribute("width", "304");
img.setAttribute("width", "328");
picture = document.getElementById("pic").appendChild(img);
setTimeout(function () {
judge();
}, 100);
}
When setting properties that way, you need to include 'px' afterward, otherwise it doesn't know what units to use with the number you're passing. Try changing the second line to img.style.marginTop = randomThingTwo + 'px';
I want to make sure that my instance of fabric.Image is always visible.
It doesn't need to be 100% visible, but a portion of it should always be seen. I've seen a few other questions that are similar and I've based my solution on them, but I haven't found anything that completely solves the problem.
My current solution works when no rotation (angles) have been applied to the image. If the angle is at 0 then this solution is perfect, but once the angle start changing I'm having problems.
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords();
var boundingRect = obj.getBoundingRect();
var max_pad_left_over_width = 50;//obj.currentWidth * .08;
var max_pad_left_over_height = 50;//obj.currentHeight * .08;
var max_top_pos = -(obj.currentHeight - max_pad_left_over_height);
var max_bottom_pos = obj.canvas.height - max_pad_left_over_height;
var max_left_pos = -(obj.currentWidth - max_pad_left_over_width);
var max_right_pos = obj.canvas.width - max_pad_left_over_width;
if(boundingRect.top < max_top_pos) {
obj.setTop(max_top_pos);
}
if(boundingRect.left < max_left_pos){
obj.setLeft(max_left_pos);
}
if(boundingRect.top > max_bottom_pos) {
obj.setTop(max_bottom_pos);
}
if(boundingRect.left > max_right_pos) {
obj.setLeft(max_right_pos);
}
});
I've created an example: https://jsfiddle.net/krio/wg0aL8ef/24/
Notice how when no rotation (angle) is applied you cannot force the image to leave the visible canvas. Now, add some rotation to the image and move it around. How can I get the rotated image to also always stay within view? Thanks.
Fabric.js provides with two object properties isContainedWithinRect and intersectsWithRect which we can use to solve the problem.
An object should not be allowed to go outside the canvas boundaries. This can be achieved if we somehow manage to make sure that the object is either inside the canvas, or at least intersects with the canvas boundaries.
For this case, since you want some portion of the image inside the canvas, we will take a rect smaller than the canvas instead of canvas boundaries to be the containing rect of the image.
Using the properties mentioned to make sure that the above two conditions are satisfied after any movement. Here's how the code looks like:
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-img');
var imgInstance = new fabric.Image(imgElement, {
top: 0,
left: 0
});
imgInstance.setScaleX(0.6);
imgInstance.setScaleY(0.6);
canvas.add(imgInstance);
var prevLeft = 0,
prevTop = 0;
var max_pad_left_over_width = imgInstance.currentWidth * .08;
var max_pad_left_over_height = imgInstance.currentHeight * .08;
var max_top_pos = max_pad_left_over_height;
var max_bottom_pos = imgInstance.canvas.height - max_pad_left_over_height;
var max_left_pos = max_pad_left_over_width;
var max_right_pos = imgInstance.canvas.width - max_pad_left_over_width;
var pointTL = new fabric.Point(max_left_pos, max_top_pos);
var pointBR = new fabric.Point(max_right_pos, max_bottom_pos);
canvas.on('object:moving', function(e) {
var obj = e.target;
obj.setCoords();
if (!obj.intersectsWithRect(pointTL, pointBR) && !obj.isContainedWithinRect(pointTL, pointBR)) {
obj.setTop(prevTop);
obj.setLeft(prevLeft);
}
prevLeft = obj.left;
prevTop = obj.top;
});
Here's the link to the fiddle: https://jsfiddle.net/1ghvjxbk/ and documentation where these properties are mentioned.
I'm creating a canvas based Select Your Own Seat booking system and having difficulty working out how to determine whether one of the circles have been clicked on.
The seats are dynamically drawn based on pulling from an array filled with XML data returned from the CRM system. I had tried to use the 'this' keyword with the thought that this would help. I thought I could use the coordinates in order to determine which seat was selected then return the ID of that seat in order to request the reservation through the CRM.
Should I create a multi-dimensional array and populate this with the coordinates and the seat ID?
document.addEventListener('DOMContentLoaded',domloaded,false);
function domloaded(){
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");
var allseats = document.getElementsByClassName("seat");
var arr = Array.prototype.slice.call(allseats);
for (var j=0; j<arr.length; j++)
{
//Get seat status
var status = arr[j].getAttribute("data-seat-status");
//Get row position
var top = arr[j].getAttribute("data-top");
//Get column position
var left = arr[j].getAttribute("data-left");
//Get seat id
var id = arr[j].getAttribute("id");
var sAngle = 0;
var eAngle = 2*Math.PI;
//Create more space between seats
left=(left*10);
top=(top*10);
var seat = function(){
function seat(x, y) {
this.color = "white";
this.x = left;
this.y = top;
this.radius = 4;
}
seat.prototype.draw = function (ctx) {
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
ctx.fill();
};
return seat;
}();
var drawSeats=new seat(top,left);
drawSeats.draw(ctx);
}
canvas.onmousedown=function findseats(arr){
var xleft=arr.clientX;
var ytop=arr.clientY;
alert("X coords: " + xleft + ", Y coords: " + ytop);
for (s=0; s<arr.length; s++){
if((xleft||((arr[s].getAttribute("data-left")*10)&&(ytop||arr[s].getAttribute("data-top")*10)))){
alert(arr[s].getAttribute("data-id"));
}
}
}}
http://jsfiddle.net/caspianturner/5sycT
A Demo: http://jsfiddle.net/m1erickson/AN4Jf/
You can listen for mousedown events and call handleMouseDown() when the event occurs:
canvas.onmousedown=handleMouseDown;
The mousedown handler gets the mouse position like this:
var canvas=document.getElementById("canvas");
var offsetX=canvas.offsetLeft;
var offsetY=canvas.offsetTop;
function handleMouseDown(e){
e.preventDefault();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
}
Assume your Seat "class" looks something like this:
// Seat "class"
function Seat(x,y,radius,id,seatNo,color){
this.x=x-200; // -200 pulls the seats leftward for better display
this.y=y;
this.radius=radius;
this.id=id;
this.seatNo=seatNo;
this.color=color
}
Then you can test if the mousedown occurred in any Seat object like this:
Seat.prototype.isMouseInside=function(mouseX,mouseY){
var dx=mouseX-this.x;
var dy=mouseY-this.y;
return(dx*dx+dy*dy<=this.radius*this.radius);
}
I have a project regarding the Dijkstra's algorithm with a canvas to draw the graph. I have the following code in the JavaScript of my html file. This is a portion of JavaScript code.
var graph = {};
var i = 0;
function ondblclick(e)
{
var cell = getpos(e)
canvasX = cell.x-10;
canvasY = cell.y-10;
graph = {i: {coord : [cell.x,cell.y]}};
var g_canvas = document.getElementById("canvas");
var context = g_canvas.getContext("2d");
var cat = new Image();
cat.src = "dot1.png";
cat.onload = function()
{
context.drawImage(cat,canvasX , canvasY);
}
}
i = i+1
function submit()
{
for (i = 0; i<= 5; i = i + 1)
alert(graph.i.coord);
}
here I have a canvas and the 'ondblclick' is the function when the user double clicks on the canvas. Canvas is used to draw nodes and edges. 'ondblclick' is used for drawing the nodes.
My issue is on saving the coordinate values of the nodes to the 'graph' in dictionary format. it accepts only the last value. How can I update/append/extend the all values to the same dictionary using the variables I and cell.x,cell.y values??
You nearly had it.
var graph = [];
function ondblclick(e)
{
var cell = getpos(e)
canvasX = cell.x-10;
canvasY = cell.y-10;
graph.push({coord : [cell.x,cell.y]});
var g_canvas = document.getElementById("canvas");
var context = g_canvas.getContext("2d");
var cat = new Image();
cat.src = "dot1.png";
cat.onload = function()
{
context.drawImage(cat,canvasX , canvasY);
}
}
function submit()
{
for (i = 0; i<= 5; i = i + 1)
alert(graph[i].coord);
}
In order to get data into graph you need to push objects into an array, then loop through that array. Your problem was that you used the i variable incorrectly, thus storing (and retrieving) your coords ontop of each other.
A dictionary (key-value store) is the wrong data structure for a graph. Use an array where you can append nodes. Use a dictionary for each element in the array to save coordinates in them.