I'm currently having this issue when I am trying to get a sprite to go up a slope within phaser using the phaser-arcade-slopes.min.js plugin. I am using a .csv tilemap also with a tile size of 32x32. I'm unsure if my names are just incorrect or I am using the wrong type of file for the tilemap itself. I have been getting errors such as - Tilemap.createLayer: Invalid layer ID given: null & Cannot read property 'resizeWorld' of undefined(…). Any help would be much appreciated.
"snow_tiles_32.png" is the name of the tileset I created and I'm using
"tiles.csv" the tilemap created inside Tiled.
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update });
function preload() {
this.game.load.tilemap('tilemap', 'assets/tilemaps/csv/tiles.csv', null, Phaser.Tilemap.CSV);
this.game.load.spritesheet('tiles', 'assets/tilemaps/tiles/snow_tiles_32.png', 32,32);
this.game.load.spritesheet('player', 'assets/penguin.png', 32,48);
}
var player;
var cursors;
function create() {
this.game.physics.startSystem(Phaser.Physics.ARCADE);
this.game.plugins.add(Phaser.Plugin.ArcadeSlopes);
cursors = game.input.keyboard.createCursorKeys();
this.map = this.game.add.tilemap('tilemap');
this.map.addTilesetImage('snow_tiles_32', 'tiles');
this.game.stage.backgroundColor = '#80e3ff';
this.ground = this.map.createLayer('collision');
this.ground.resizeWorld();
this.game.slopes.convertTilemapLayer(this.ground,{
2: 'FULL',
3: 'HALF_BOTTOM_LEFT',
4: 'HALF_BOTTOM_RIGHT',
6: 'HALF_TOP_LEFT',
5: 'HALF_TOP_RIGHT',
15: 'QUARTER_BOTTOM_LEFT_LOW',
16: 'QUARTER_BOTTOM_RIGHT_LOW',
17: 'QUARTER_TOP_RIGHT_LOW',
18: 'QUARTER_TOP_LEFT_LOW',
19: 'QUARTER_BOTTOM_LEFT_HIGH',
20: 'QUARTER_BOTTOM_RIGHT_HIGH',
21: 'QUARTER_TOP_RIGHT_HIGH',
22: 'QUARTER_TOP_LEFT_HIGH',
23: 'QUARTER_LEFT_BOTTOM_HIGH',
24: 'QUARTER_RIGHT_BOTTOM_HIGH',
25: 'QUARTER_RIGHT_TOP_LOW',
26: 'QUARTER_LEFT_TOP_LOW',
27: 'QUARTER_LEFT_BOTTOM_LOW',
28: 'QUARTER_RIGHT_BOTTOM_LOW',
29: 'QUARTER_RIGHT_TOP_HIGH',
30: 'QUARTER_LEFT_TOP_HIGH',
31: 'HALF_BOTTOM',
32: 'HALF_RIGHT',
33: 'HALF_TOP',
34: 'HALF_LEFT'
});
this.map.setCollisionBetween(2,34, true, 'collision');
//player
this.player = this.game.add.sprite(100,50,'player');
this.game.physics.arcade.enable(player);
this.player.body.bounce.y = 0.2;
this.player.body.gravity.y = 2000;
this.player.body.collideWorldBounds = true;
this.player.animations.add('left', [0,1,2,3], 10, true);
this.player.animations.add('right', [5,6,7,8], 10, true);
this.game.slopes.enable(this.player);
this.game.camera.follow(this.player);
}
function update() {
this.game.physics.arcade.collide(this.player, this.ground);
this.player.body.velocity.x = 0;
if(cursors.left.isDown){
this.player.body.velocity.x = -150;
this.player.animations.play('left');
}
else if (cursors.right.isDown){
this.player.body.velocity.x = 150;
this.player.animations.play('right');
}
else{
this.player.animations.stop();
this.player.frame = 4;
}
if(cursors.up.isDown && player.body.onFloor()){
this.player.body.velocity.y = -350;
}
}
LayerID you provide as parameter for createLayer() must match map layer defined in map file, see documentation. So my best guess would be to open map file in tiled once again and check what layer names you have.
Additionally there's example how to use .csv files (here)[http://phaser.io/examples/v2/tilemaps/csv-map].
Hope that'll help somehow.
Related
I'm creating a calculator that takes weight, location, and quantity as a parameter. I have the prices set as key value pairs, and when a weight is entered it will iterate over the object array, find the key that is equal to it, and display a message of the price using the value. The problem i'm running into is whenever a number is entered that isn't a whole number, nothing is displayed back. The following is my code:
const calculateRate = () => {
let price
let ausWeight = {
1: 19, 2: 25, 3: 25, 4: 28, 5: 28, 6: 31.5, 7: 35, 8: 38.5, 9: 42, 10: 45.5, 11: 49, 12: 52.5, 13: 56, 14: 59.5,
15: 63, 16: 66.5, 17: 70, 18: 73.5, 19: 77, 20: 80.5};
let inputWeight = Object.keys(ausWeight)
let inputValue = Object.values(ausWeight)
let x = Math.floor(weight);
console.log(x)
for (var i = 0; i < inputWeight.length; i++){
if (x === inputWeight[i] && region.value === 'Australia') {
price = `Estimated delivery cost is $${inputValue[i] * quantity}`;
setShowResults(true)
setPrice(price);
}
}
}
As you can see, i'm setting x to equal to weight rounded down with Math.floor(), but i'm not getting an output. You can also see that i'm logging the value of x to the console and it's giving the result i'm expecting when i look at it through dev tools. For context, this is a functional react component, with weight being set by setState().
Any idea's on how to fix this would be appreciated. Thanks.
You simply compare a string with a number.
By using Object.keys, you get an array of strings. But later you take a strict comparing.
To overcome this, you need to convert the string value into a number
x === +inputWeight[i]
I am Iterating through an array of locations that have longitude and latitude values. I am using these values to call the Open Weather API asynchronously. I am then looping through the returned JSON array to grab each location's forecast date and time, forecast description, and the forecast icon. Then I want to store them in an array.
My issue is when I try to push the data back to the 'forecastArray' (so I can place the data into my HTML dynamically with jQuery) I can't access the data, when I console.log it the browser it returns as 'undefined'. the actual array will log to the browser but I can't assign the data to variables.
I am fairly new to web development so I would appreciate any advice given. I must not be understanding the scope limitations.
Here is my code:
let ForcastArray = []
for (let i = 0; i < cities1Array.length; i++) {
// console.log(citiesArray1[i])
let lat = citiesArray1[i][0]
let lng = citiesArray1[i][1]
openWeatherForcast(lat, lng).then(forcast => {
// console.log(forcast)
for (let j = 0; j < 10; j++) {
// console.log(forcast['list'][j]['dt_txt'])
let dateTime = forcast['list'][j]['dt_txt']
// console.log(forcast['list'][j]['weather'][0]['description'])
let description = forcast['list'][j]['weather'][0]['description']
// console.log(forcast['list'][j]['weather'][0]['icon'])
let iconcode = forcast['list'][j]['weather'][0]['icon'];
let iconurl = "http://openweathermap.org/img/wn/" + iconcode + "#2x.png";
ForcastArray.push(dateTime, description, iconurl)
}
})
}
console.log(ForcastArray)
// Returns:
// []
// 0: "2020-09-27 12:00:00"
// 1: "few clouds"
// 2: "http://openweathermap.org/img/wn/02d#2x.png"
// 3: "2020-09-27 15:00:00"
// 4: "clear sky"
// 5: "http://openweathermap.org/img/wn/01d#2x.png"
// 6: "2020-09-27 18:00:00"
// 7: "clear sky"
// 8: "http://openweathermap.org/img/wn/01n#2x.png"
// 9: "2020-09-27 21:00:00"
// 10: "few clouds"
// 11: "http://openweathermap.org/img/wn/02n#2x.png"
// 12: "2020-09-28 00:00:00"
// 13: "scattered clouds"
// 14: "http://openweathermap.org/img/wn/03n#2x.png"
// 15: "2020-09-28 03:00:00"
// 16: "light rain"
// 17: "http://openweathermap.org/img/wn/10n#2x.png"
// 18: "2020-09-28 06:00:00"
// 19: "light rain"
// 20: "http://openweathermap.org/img/wn/10d#2x.png"
// 21: "2020-09-28 09:00:00"
// 22: "moderate rain"
// 23: "http://openweathermap.org/img/wn/10d#2x.png"
// 24: "2020-09-28 12:00:00"
// 25: "light rain"
// 26: "http://openweathermap.org/img/wn/10d#2x.png"
// 27: "2020-09-28 15:00:00"
// 28: "overcast clouds"
// 29: "http://openweathermap.org/img/wn/04d#2x.png"
// length: 30
// __proto__: Array(0)
console.log(ForcastArray[0])
// Returns:
// undefined
If the array is populated you should be able to access it with the array access operator (the square brackets) [0] like you wrote (console.log(ForcastArray[0]).
As Deniz pointed out this is probably a timing issue. You should put the console.logs inside your .then() block, the array will be accessible when this block is executed.
openWeatherForcast(lat, lng).then(forcast => {
for (let j = 0; j < 10; j++) {
// console.log(forcast['list'][j]['dt_txt'])
let dateTime = forcast['list'][j]['dt_txt']
// console.log(forcast['list'][j]['weather'][0]['description'])
let description = forcast['list'][j]['weather'][0]['description']
// console.log(forcast['list'][j]['weather'][0]['icon'])
let iconcode = forcast['list'][j]['weather'][0]['icon'];
let iconurl = "http://openweathermap.org/img/wn/" + iconcode + "#2x.png";
ForcastArray.push(dateTime, description, iconurl)
}
console.log(ForcastArray[0])
})
You can count the returned promises and see if they match the number of cities (length of cities1Array or citiesArray1). When returned promises == citiesArray1.length you can execute another function to start the DOM manipulation (insert the data in your HTML via jQuery).
I'm currently working on an idle game and have encountered a problem.
I'm trying to create a function that will lessen the value of 1 of 6 variables (each represents a type of worker) a number of times. However, if one of these 6 variables is at 0, I need it to pick one of the other 5, and I can't get that to work.
With fiddling around, I have gotten this:
function killWorker(amount){
var job;
for (var i=0;i<amount;i++){
job = Math.floor((Math.random()*6)+1);
switch (job){
case 1:
if(unass_workers>0){
unass_workers -= 1;
}else{
i-=1;
}
case 2:
if(farm_workers>0){
farm_workers -= 1;
}else{
i-=1;
}
break;
case 3:
if(tree_workers>0){
tree_workers -= 1;
}else{
i-=1;
}
break;
case 4:
if(metMine_workers>0){
metMine_workers -= 1;
}else{
i-=1;
}
break;
case 5:
if(golMine_workers>0){
golMine_workers -= 1;
}else{
i-=1;
}
break;
case 6:
if(paper_workers>0){
paper_workers -= 1;
}else{
i-=1;
}
break;
default:
console.log("error: killWorker() didn't work properly");
break;
}
}
}
This worked when I did low amounts, but when I increased the amount higher the whole thing crashed. I'd be quite happy to drastically change the function if it would work better, and I am using jquery too if that could help get an easier or more effective solution.
As i said, what probably happens is that amount is larger than the sum of X_workers, resulting in an infinite loop.
A fix that keeps your logic correct would be to check that enough workers can be killed:
function killWorker(amount){
if (amount < unass_workers + farm_workers + ...) return "Genocide";
var job;
for (var i=0;i<amount;i++){
...
}
A better way to organize your data structures could be:
var workerNames = [ 'unass', 'farm', 'tree', 'metMine', 'golMine', 'paper' ];
var workers = {
unass : 23,
farm : 45,
tree : 6,
metMine : 99,
golMine : 3,
paper: 43
}
function getRandomLiveJobName() {
var jobs = workerNames.filter(function(name) {
return workers[name] > 0;
});
return jobs[Math.floor(Math.random()*jobs.length)];
}
function killWorkers(amount) {
for (;amount--;) {
var job = getRandomLiveJobName();
if (!job) return "Genocide";
workers[job]--;
}
}
killWorkers(150);
console.log(workers); // { farm: 0, golMine: 0, metMine: 64, paper: 5, tree: 0, unass: 0 }
Of course it could be optimized by not creating a new array each time you need a random live job by updating a single array inside killWorkers, but i think it's easier to read now.
I have two variables (the enemies and players helath) that change when a certain event happens, the problem is that I dont know how to change text, or remove text, so I cant change the displayed health on the screen (currently I am just placing the new health ontop of the old one). Here is the relevant component.
//player character during combat
//This also controls player input during combat and resolves most of the combat
//Will display and take inputs for quesions eventually
Crafty.c('BattlePlayer', {
init:function() {
var OpPos = 1;
var HealthPas = 100 + ',' + 100;
var FightOver = false;
//Displays options for the first time with attack selected
this.requires('Actor, spr_BattlePlayer, SpriteAnimation, Keyboard')
.bind('KeyDown', function () { if (this.isDown('SPACE')) Crafty.scene('World');})
.bind('KeyDown', function () { if (this.isDown('ENTER')) HealthPas = this.BattleSelect(OpPos, HealthPas, FightOver);})
.bind('KeyDown', function () { if (this.isDown('S')) if (OpPos < 3){OpPos = OpPos + 1}; this.MenuMove(OpPos); })
.bind('KeyDown', function () { if (this.isDown('W')) if (OpPos > 1) {OpPos = OpPos - 1}; this.MenuMove(OpPos); });
Crafty.e('2D, DOM, Text')
.attr({ x: 200, y: 150 })
.text(100);
Crafty.e('2D, DOM, Text')
.attr({ x: 200, y: 100 })
.text(100);
Crafty.e('AttackSel').at(3,8);
Crafty.e('DefendUnsel').at(3,13);
Crafty.e('RunUnsel').at(3,18);
var MenuPos = 1;
},
//function for displaying what option is currently selected
MenuMove: function(OpPos) {
switch (OpPos)
{
case 1:
//Attack case
Crafty.e('AttackSel').at(3,8);
Crafty.e('DefendUnsel').at(3,13);
Crafty.e('RunUnsel').at(3,18);
break;
case 2:
//Defend case
Crafty.e('AttackUnsel').at(3,8);
Crafty.e('DefendSel').at(3,13);
Crafty.e('RunUnsel').at(3,18);
break;
case 3:
//Run case
Crafty.e('AttackUnsel').at(3,8);
Crafty.e('DefendUnsel').at(3,13);
Crafty.e('RunSel').at(3,18);
break;
default:
//Incorrect input case
Crafty.e('AttackUnsel').at(3,8);
Crafty.e('DefendUnsel').at(3,13);
Crafty.e('RunUnsel').at(3,18);
}
},
//function for carrying out battle options
//Within this function Num[0] represents players health and Nums[1] represents the Enemy Health.
BattleSelect: function(OpPos, HealthPas, FightOver) {
var Nums = HealthPas.split(',');
Crafty.e('2D, DOM, Text')
.attr({ x: 200, y: 150 })
.text(Nums[0]);
switch (OpPos)
{
case 1:
//Attack case
if (FightOver == false)
{
Nums[1] = Nums[1] - 20;
Crafty.e('2D, DOM, Text')
.attr({ x: 200, y: 100 })
.text(Nums[1]);
Crafty.audio.play('attack');
this.EndCheck(Nums[0], Nums[1], FightOver);
Nums[0] = Nums[0] - 10;
}
break;
case 2:
//Heal case
//as this was originaly a string, minus 1 to change it to an integer
Nums[0] = Nums[0] - 1 + 21;
this.EndCheck(Nums[0], Nums[1], FightOver);
break;
case 3:
//Run case
//switch checks what room the player is in the transport them back there
switch (Location)
{
case 1:
Crafty.scene('World');
break;
case 2:
Crafty.scene('HubWorld');
break;
case 3:
Crafty.scene('Yr10Tp1');
break;
case 3.1:
Crafty.scene('Yr10Tp1_1');
break;
case 3.11:
Crafty.scene('Yr10Tp1_1.1');
break;
case 3.12:
Crafty.scene('Yr10Tp1_1.2');
break;
case 3.2:
Crafty.scene('Yr10Tp1_2');
break;
case 3.3:
Crafty.scene('Yr10Tp1_1.3');
break;
case 4:
Crafty.scene('Yr10Tp2');
break;
case 5:
Crafty.scene('Yr10Tp3');
break;
case 6:
Crafty.scene('Yr11Tp1');
break;
case 7:
Crafty.scene('Yr11Tp2');
break;
case 8:
Crafty.scene('Yr11Tp3');
break;
default:
Crafty.scene('World');
}
default:
this.EndCheck(Nums[0], Nums[1]);
Nums[0] = Nums[0] - 10;
}
HealthPas = Nums[0] + ',' + Nums[1];
return HealthPas;
},
//function to check for winning conditions
EndCheck : function(PlayerHealth, EnemyHealth, FightOver)
{
if (EnemyHealth < 1)
{
FightOver = true;
monsterBeat = true;
}
else if (PlayerHealth < 1)
{
FightOver = true;
}
if (FightOver == true)
{
Crafty.e('2D, DOM, Text')
.attr({ x: 500, y: 150 })
.text('Victory!');
}
return FightOver;
},
});
Make sure to look at the documentation for text.
To set the text directly, you just call the text method of the entity. So whenever the base property changes, you could do this -- either by calling the method directly, or setting up an "UpdateHealth" event.
So, a text component might look like this:
Crafty.e('2D, DOM, Text')
.attr({ x: 200, y: 150 })
.text(Nums[0]);
.bind("UpdatePlayerHealth", function(health){this.text(health)});
And then when the player took damage, it would trigger that event with code that looks something like this:
Crafty.trigger("UpdatePlayerHealth", this.health);
The advantage of using events like this is that you don't need to carry around a reference to the text object, and you could modify other aspects of the game when a player's health changed.
If you wanted, you could pass more info in the event by using an object, such as:
Crafty.trigger("UpdatePlayerHealth", {health: this.health, damage: damageValue});
(Although of course you'd also need to adjust the bound function on the text display, since it would be passed an object rather than just a number.)
I am using fluster 2 to cluster markers and have found that the fluster clustering method is overly aggressive. So that even when I am zoomed in to city level I still end up having a lot more clusters than I really want. Does anyone know how to modify the fluster .js to make it show all markers once you get down to say zoom level 5?
There is a bit too much code relating to fluster to add here, so this question is more for people who already use fluster for their marker clustering needs.
Thank you,
you can make a listener on the zoom level of your map and then control the clusters. this an example of what probably your code shoud look like. I havent tested the code , the comple tutorial from which i took the code is here.
var fluster = new Fluster2(map);
// Set styles for the clustered icons
fluster.styles = {
0: {
image: "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m1.png",
textColor: "#FFFFFF",
width: 53,
height: 52
},
10: {
image: "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m2.png",
textColor: "#FFFFFF",
width: 56,
height: 55
},
15: {
image: "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/images/m3.png",
textColor: "#FFFFFF",
width: 66,
height: 65
}
};
// Initialize Fluster
// This will set event handlers on the map and calculate clusters the first time.
fluster.initialize();
// Event listener for switching to Street View/Road Map to show/hide the
// close button, respectively
google.maps.event.addListener(panorama,"visible_changed",toggleVisible);
// Add a listener to the zoom change event so we can change the grid size
// of the cluster script. Should be dynamic!
google.maps.event.addListener(map, "zoom_changed", function() {
var zoomLevel = map.getZoom();
switch(true){
case zoomLevel > 13:
fluster.gridSize = 0;
break;
case zoomLevel > 12:
fluster.gridSize = 10;
break;
case zoomLevel > 7:
fluster.gridSize = 20;
break;
case zoomLevel > 5:
fluster.gridSize = 40;
break;
default:
fluster.gridSize = 60;
break;
}
});
Good answer Zied.
BTW if you have multiple markers with the same position, the clustering will still happen even though the gridSize is 0.
To get around this set the grid to -1
google.maps.event.addListener(map, "zoom_changed", function() {
var zoomLevel = map.getZoom();
switch(true){
case zoomLevel > 13:
fluster.gridSize = -1;
break;
case zoomLevel > 12:
fluster.gridSize = 10;
break;
case zoomLevel > 7:
fluster.gridSize = 20;
break;
case zoomLevel > 5:
fluster.gridSize = 40;
break;
default:
fluster.gridSize = 60;
break;
}
});