I'm very new to Javascript, and what I'm about to ask is probably very rudimentary, but I'm stuck on a project, and I'd like some help.
Basically I'm doing a small Javascript project, where I want there to be some enemies. In the beginning there will be 0 enemies, and then as time goes on, there might be created more enemies. I was thinking that whenever an enemy is created, I should create it as an object, and then store it in an array containing all the active enemies in game. Then, later on, I could remove enemies from the array, that needed to be removed.
This is the function that creates my enemy objects:
function enemy(name, strength, rarity, estTime, success, remTime, Id){
this.name = name;
this.strength = strength;
this.rarity = rarity;
this.estTime = estTime;
this.success = success;
this.remTime = remTime;
this.Id = Id;
}
So, I could then create some enemies like this:
var enemy1 = new enemy("Bob", 1, "Common", 100, 1, 30, 1)
var enemy2 = new enemy("Cow", 22, "Rare", 50, 10, 40, 2)
var enemy3 = new enemy("Pig", 333, "Epic", 25, 10, 50, 3)
Then I could create an array of enemies, and put my 3 enemies into that array:
var enemies = [];
enemies = [enemy1, enemy2, enemy3];
All fine and dandy when I'm doing this manually, but the problem emerges when I want to try to get the code to automatically create some more enemies.
Say I wanted to create an enemy everytime a button was pushed by the user. The name enemy would get some name, strength, rarity whatever, and the next Id in line (in this case 1, 2 and 3 are being used, so the next would be 4).
I was thinking I could do it something like this, but this doesn't work:
enemy[enemies.length + 1] = new enemy("Dog", 444, "Epic", 13, 100, 60, 4);
enemies.push(enemy + [enemies.length + 1]);
I was hoping that this would create an object called "enemy4" with with name, id and whatever I just typed in, and then add that object to the array of enemies.
But this obviously doesn't work. I hope you guys understand the problem, and any help is greatly appreciated. I realize that I'm probably just approaching this all wrong, and that there probably exists a much simpler way to do this.
Thanks!
EDIT: Yep, answer was very simple, got it now. Thanks!
var enemies = [
new enemy(/*params here*/),
new enemy(/*params here*/),
new enemy(/*params here*/),
new enemy(/*params here*/),
new enemy(/*params here*/)
];
Then later:
enemies.push(
new enemy(/*params here*/)
);
Related
I'm creating an animation with 5 objects using createjs and tween.
The first object is a car that cross horizontally the screen and the other 4 objects are 4 buildings that I want to show once at a time every time the car pass over the object (that is initially hidden).
This is part of the code where i set the position and the alpha to 0.
Truck is the object crossing the screen blocks are the building I want to show one at time.
truck = new createjs.Shape();
truck = new createjs.Bitmap(loader.getResult("truck"));
truck.x=-truck.image.width;
truck.y=0;
createjs.Tween.get(truck, { loop: true })
.wait(250)
.to({ x: w }, 5600, createjs.Ease.getPowInOut(0.5));
block1 = new createjs.Bitmap(loader.getResult("block3"));
block1.x=0;
block1.y=truck.image.height-block1.image.height;
block1.alpha=0.0;
createjs.Tween.get(block1, { override: true })
.wait(250)
.to({ alpha: 1 }, 600, createjs.Ease.getPowInOut(0.5));
block2 = new createjs.Bitmap(loader.getResult("block2"));
block2.x=0;
block2.y=truck.image.height-block2.image.height-block1.image.height;
block2.alpha=0.0;
createjs.Tween.get(block2, { override: true })
.wait(500)
.to({ alpha: 1 }, 1600, createjs.Ease.getPowInOut(0.5));
block3 = new createjs.Bitmap(loader.getResult("block1"));
block3.x=block1.image.width;
block3.y=truck.image.height-block3.image.height;;
block3.alpha=0.0;
block4 = new createjs.Bitmap(loader.getResult("block4"));
block4.x=block1.image.width+block3.image.width;
block4.y=truck.image.height-block4.image.height;;
block4.alpha=0.0;
block5 = new createjs.Bitmap(loader.getResult("block5"));
block5.x=block1.image.width+block3.image.width+block4.image.width;
block5.y=truck.image.height-block5.image.height;;
block5.alpha=0.0;
Use this
var index = truck.parent.getChildIndex(truck);
in order to get the index of the truck object. Then, you will need to set the index of the objects you want to show above, using this:
block.parent.setChildIndex(block, index + 1);
Alternatively, you can remove and then re-add an object in order to get it to be above (highest index) of all the other objects on the canvas:
var parent = block.parent;
parent.removeChild(block);
parent.addChild(block);
I use an HTML button to call a function :
<button id="attackButton" type="button" onclick="Enemy.prototype.fight(Test)"> Attaquer </button>
This function is :
Enemy.prototype.fight = function(Enemy)
{
}
This function is linked to these 2 variables :
var Enemy = function (damage, life)
{
this.damage = damage;
this.life= life;
};
var Test= new Enemy (7,20);
I would like to make something which will replace the parameter"Test" by "Test2" within the HTML Button so my button will now redirect to the same function while using a third variable :
var Test2= new Enemy (10,30);
I hope I was clear enough. Thank you. :)
EDIT :
To clarify : A player is fighting different enemies. Enemies have different variables (armor, life, damage etc...) The fight is a function and variables varies based on the parameter. The html button calls that function.
The player is going to make various actions within the game and sometimes he will fight. Sometimes he will not.
I would like to keep one single html button which always call the same function. But the parameter has to change each time I want it.
For instance :
If you take the left side, then you fight Enemy 1.
If you take the right side, then you fight Enemy 2.
Right now the full the full Enemy function is
var Enemy = function (damage, initiative, armor, lifeNow, life)
{
this.damage = damage;
this.initiative = initiative;
this.armor = armor;
this.lifeNow = lifeNow;
this.life = life;
};
and each time I want to add a different enemy I write this :
var enemy1= new Enemy (7, 9, 3, 15, 15);
var enemy2= new Enemy (50, 9, 3, 100, 100);
Hope it helps :p Thank you again
I'm going to have to make a couple of assumptions here. The main one is that you have a number of Enemy objects and you want to attack each one in sequence.
First, use an array, not sequentially named variables.
var enemies = [ new Enemy (7,20), new Enemy (10,30) ];
Second, track which enemy is next to be attacked:
var nextEnemy = 0;
Third, refactor your code to make the attack enemy logic a function of its own.
Attack the current next enemy and then work out which one should be attacked after that.
function attackNextEnemy() {
Enemy.prototype.fight(enemies[nextEnemy]);
nextEnemy++;
if (!enemies[nextEnemy]) {
nextEnemy = 0; // back to the top
}
}
You can then call that function from onclick.
There are various other things you could to that would be better practises (such as mentioned in dfsq's comment, using addEventListener instead of HTML attributes), and putting the nextEnemy variable inside a closure to keep it private, but a Stackoverflow answer isn't the place to go into that level of depth.
After the question was updated:
If you take the left side, then you fight Enemy 1. If you take the right side, then you fight Enemy 2.
In that case, you probably don't want to use an array. You are dealing with positions not sequences.
An object such as:
var enemies = {
left: new Enemy (7,20),
right: new Enemy (10,30)
};
… would make more sense.
Then you would need to change your attack to:
var sideThePlayerIsOn = "left";
and
Enemy.prototype.fight(enemies[sideThePlayerIsOn]);
as well as adding some logic to change the sideThePlayerIsOn variable based on whatever the player can do to change that.
You can extract the variable from the button click event into a kind of context like the game in my example and in this way you can change the enemies any time you want.
For example if the side is changed just call: changeEnemy(game1, enemy2);
var Enemy = function (damage, initiative, armor, lifeNow, life)
{
this.damage = damage;
this.initiative = initiative;
this.armor = armor;
this.lifeNow = lifeNow;
this.life = life;
};
Enemy.prototype.fight = function(enemy) {
alert('it works!' + enemy.damage);
}
var Game = function (enemy1, enemy2)
{
this.enemy1 = enemy1;
this.enemy2= enemy2;
this.fight = function () {
this.enemy1.fight(this.enemy2);
}
};
var enemy1= new Enemy (7, 9, 3, 15, 15);
var enemy2= new Enemy (50, 9, 3, 100, 100);
var enemy3= new Enemy (60, 9, 3, 100, 100);
var game1= new Game(enemy1, enemy2);
function changeEnemy(game, newEnemy) {
game.enemy2 = newEnemy;
}
changeEnemy(game1, enemy3);
<button id="attackButton" type="button" onclick="game1.fight();"> Attaquer </button>
I'm trying to change the length of a cylinder or the extrudedHeight of a circle when it has been added to the primitives and is shown in the cesium widget/viewer. For example this cylinder:
var length = 100;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
var primitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
widget.scene.primitives.add(primitive);
Because it's added to the primitives array it will be shown in the widget, but after 2 seconds for example I get a notification that the length should be halved (that means set to 50). Is there any way to do this? Simply changing it in cylinderGeometry doesn't seem to do the job.
I kind of have it working by creating a new cylinder with the new height, adding it and removing the old one. This however tends to flicker the cylinder (it's gone for a fraction of a second) before the new one is shown. I fixed this problem by removing the old instance after a set time after the new one is added. This whole solution isn't very elegant and doesn't work very well on devices with a small amount of computing power, hence my search for a better solution.
I don't care if this is achieved using cylinders or extruded circles. If you need any more information don't hesitate to ask in the comments below the question.
EDIT
I implemented the second solution Matthew suggested but after a while of it running perfectly the cylinders stop changing height (which didn't occur when I used my solution. The callback in the interval does get called. Here is some code showing what my new solution is (not working):
primitives.add(prim);
window.nodeValuesInterval = setInterval(function () {
if (prim._state == Cesium.PrimitiveState.COMPLETE) {
clearInterval(window.nodeValuesInterval);
clearTimeout(window.nodeValuesTimeout);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}
}, cylindervalueinterval);
window.nodeValuesTimeout = setTimeout(function () {
clearInterval(window.nodeValuesInterval);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}, cylindervaluedelay);
Cesium's geometry is currently optimized for static data. Some attributes, such as visibility, color, and material can be changed on the fly, but items that actually modify the geometry (like cylinder height) require you to remove the primitive and recompute the geometry. The flickering your seeing is the result of asynchronous primitive creation being on by default. There are two ways to do want you want.
Disable asynchronous primitive create by passing [options.asynchronous: false to the Primitive constructor. This means that when you add a new primitive, Cesium will not render until it is ready. For one or two objects, you won't notice anything. For lots of objects it will lock up the browser until everything is ready. This does guarantee that you can remove old/add new primitives without any flicker.
The second option is to add your new primitive (without removing the old one) and then every frame, check the _state property of your new Primitive (I thought this was part of the public API but apparently it's not). When the _state is equal to Cesium.PrimitiveState.COMPLETE you can safely remove the old primitive and your guaranteed the new one will render (hence no flicker).
I think we have a bug/feature request to expose the state variable publicly or otherwise notify when the Primitive is ready; but using _state should be fine for the forseeable future. I'll update this issue if we add an official way sometime soon.
Hope that helps.
EDIT: Since more help was requested; here's a complete example. You can copy and paste the below code into Sandcastle using this link.
Basically it uses the scene.preRender event instead of a timeout (preRender is almost always the better answer here). Also, if you receive a new update before the old one is finished processing, it's important to remove that one before computing the new one. Let me know if you are still having problems.
require(['Cesium'], function(Cesium) {
"use strict";
var widget = new Cesium.CesiumWidget('cesiumContainer');
var ellipsoid = widget.scene.globe.ellipsoid;
var lon = 0;
var lat = 0;
var cylinderradius = 30000;
var length = 10000000;
var cylinderslices = 32;
var newPrimitive;
var currentPrimitive;
//This function creates a new cylinder that is half the length of the old one.
function decreaseLength() {
//If there's a pending primitive already, remove it.
if(Cesium.defined(newPrimitive)){
widget.scene.primitives.remove(newPrimitive);
}
length /= 2;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
newPrimitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
//We add the new cylinder immediately, but don't remove the old one yet.
widget.scene.primitives.add(newPrimitive);
}
//Create the initial cylinder.
decreaseLength();
//Subscribe to the preRender event so we can check the primitive every frame.
widget.scene.preRender.addEventListener(function(scene, time) {
//Remove the old cylinder once the new one is ready.
if(Cesium.defined(newPrimitive) && newPrimitive._state === Cesium.PrimitiveState.COMPLETE){
if(Cesium.defined(currentPrimitive)){
widget.scene.primitives.remove(currentPrimitive);
}
currentPrimitive = newPrimitive;
newPrimitive = undefined;
}
});
Sandcastle.addToolbarButton('Decrease Length', decreaseLength);
Sandcastle.finishedLoading();
});
With a lack of math neurons in my brain, i struggle a bit in finding my way around this.
I need to create a simple javascript function that will receive three parameters:
A one-dimensional, normal indexed Array with X elements (the values being unique IDs)
A target ID to select
An amount of elements to return
The third parameter would ask the function to return a set of elements, with the element having the target ID being either in the center of the result, or next to it.
The result of the function should be an array as well.
A few examples to make it a more visual explanation:
function([100,120,140,160,180,200], 120, 3)
// should return [100,120,140]
function([100,120,140,160,180,200], 160, 4)
// should return [140,160,180,200]
function([100,120,140,160,180,200], 180, 5)
// should return [140,160,180,200,100]
The case covered by the last example is what confuses me while writing the code, which i am currently attempting to, but i find myself writing strange conditions, numerous if-statements and code that generally seems like a work-around. Also the cases of parameter 3 being larger than the amount of elements in parameter 1 are a bit of an over-brainer for me.
I feel unsafe continuing with this code, because it feels buggy and simply not proper. Surely somebody with proper math skills could provide me with the theory i need to understand how to accomplish this in a more elegant fashion.
Theory or pseudo-code will suffice, but if someone has something like this ready at hand, please don't hesitate to share it.
Thank You!
(Here is what i have written so far - based on the prototype JS class implementation)
var CBasicMatrix=Class.create({
initialize: function(elementList){
this.elementList=elementList;
},
select: function(id, amount){
if(amount>this.elementList.length)
amount=this.elementList.length;
if(!this.elementList.length) return false;
var elementIndex=this.elementList.indexOf(id);
if(elementIndex==-1) return false;
var isRound=amount%2==0;
var amountHalf=isRound ? (amount/2) : (Math.ceil(amount/2)-1);
// [464,460,462,461,463]
var result=[];
if(elementIndex-amountHalf >= 0) {
var startIndex=(elementIndex-amountHalf);
for(i=startIndex;i<=startIndex+amount;i++){
result.push(this.elementList[i];
}
} else {
// more seemingly stupid iterative code coming here
}
}
});
Edit: In order to make this more understandable i will state the purpose. This code is supposed to be used for kind of a slideshow, in which multiple elements (parameter 3) are visible at the same time. Parameter 1 is the list of (the IDs of the) total elements in their correct order as they appear in the HTML declaration. Parameter 2 is the element that is currently selected and therefore should appear in the middle.
Here is my solution:
function method(arr, value, n) {
var result = [],
len = arr.length,
index = arr.indexOf(value);
for (var i = 0; index > -1 && i < n ; i++) {
result.push(arr[(len + index - ~~(n / 2) + (n % 2 ^ 1) + i) % len]);
}
return result;
}
TESTS:
var arr = [100, 120, 140, 160, 180, 200];
method(arr, 120, 3); // [100, 120, 140]
method(arr, 160, 4); // [140, 160, 180, 200]
method(arr, 180, 5); // [140, 160, 180, 200, 100]
method(arr, 100, 3); // [200, 100, 120]
I will help you by providing a pseudo code :
1 . if there is no match you should return an empty array.
2 . if there is a match you just divide the third parameter by 2, you take the result , you loop from the element found's index minus the previous result until the third parameter's value and you store the elements in a new array.
3 . you return the new array.
Update:
I saw your code and I don't see any problem with it.
After some careful debugging and overthinking my approach, i managed to find a solution that seems proper and safe. I am sure this could be optimised further and if anyone has any suggestions, feel free to share them.
var CBasicMatrix=Class.create({
initialize: function(elementList){
this.elementList=elementList;
},
select: function(id, amount){
if(amount>this.elementList.length)
amount=this.elementList.length;
if(!this.elementList.length) return false;
var elementIndex=this.elementList.indexOf(id);
if(elementIndex==-1) return false;
var isRound=amount%2==0;
var amountHalf=isRound ? (amount/2) : (Math.floor(amount/2));
var result=[];
var startIndex=(elementIndex-amountHalf);
var endIndex=(startIndex+amount-1);
var targetIndex=0;
for(i=startIndex;i<=endIndex;i++){
targetIndex=i;
if(i>this.elementList.length-1) targetIndex=i-this.elementList.length;
if(i<0) targetIndex=i+this.elementList.length;
result.push(this.elementList[targetIndex]);
}
return result;
}
});
In raphael, if I want to render the following shape:
I have to do something like:
var paper = Raphael("notepad", 320, 200);
var rect = paper.rect(...);
var line1 = paper.path(...);
var line2 = paper.path(...);
which create three elements: rect, line1, line2.
BUT, I would like to treat the rendered shape as one object in other js code insteand of three. In Raphael, how can I create this shape which returns me just one object not three?
You want to create a set.
Creates array-like object to keep and operate couple of elements at once. Warning: it doesn't create any elements for itself in the page.
var st = paper.set();
st.push(
paper.circle(10, 10, 5),
paper.circle(30, 10, 5)
);
st.attr({fill: "red"});
Your code would look something like this:
var paper = Raphael("notepad", 320, 200),
st = paper.set();
st.push(
paper.rect(...),
paper.path(...),
paper.path(...)
);
// use st elsewhere
Edit
How can I access individual element in the set then?
You can grab references to the objects before you add them to the set:
var paper = Raphael("notepad", 320, 200),
st = paper.set(),
rect1 = paper.rect(...),
path1 = paper.path(...),
path2 = paper.path(...);
st.push(rect1, path1, path2);
I'm not 100% sure, but since the docs say that a set is "array-like," then you should also be able to access elements in the set using array index notation:
var i, elt;
for (i=0; i<st.length; i++)
{
elt = st[i];
// do stuff with elt
}