I'm taking an adventure into the depths of JavaScript and have come across a little problem that I can't get my head around.
Everything I know about programming is self taught, this problem might have some terminology behind it I have never heard of, so I don't know what it would be called.
I'll explain the problem I am experiencing.
I've been writing a framework for HTML5 canvas for displaying 2d and 3d graphics.
As you might expect, I have designed an element class, these elements have positions on the canvas which are built from a vector class I put together.
The problem I'm having is, if I make two "Text" objects, then call a function inside their position object, all the positions of the "Text" objects change to this value:
var usernameLabel = new C.Text('Username:');
usernameLabel.position.set(30,30)
var username = new C.Text('Hello World');
username.position.set(0,70)
console.log(usernameLabel.position.x) // 0 when it should be 30
I'm sure there is something I missed, I just can't figure out what.
C.Text.prototype = new C.Element();
C.Element.position = new JC.Vector();
Any help would be most appreciated!
This is my full Element class
C.elements = 0;
C.Element = function()
{
this.id = C.elements ++;
this.position = new C.Vector();
this.rotation = new C.Vector();
this.style = new C.Style();
this.children = [];
}
C.Element.prototype = {
constructor : C.Element,
addChildObject : function( o )
{
return this.children.push(o);
},
removeChildObject : function( o )
{
this.children.splice(o,1);
}
}
Text class
C.Text = function(string)
{
this.string = string || '';
}
C.Text.prototype = new C.Element();
C.Text.prototype.constructor = C.Text();
I also have more classes built from C.Element obviously, for example:
C.Rectangle = function(width, height)
{
this.style.setSize(width,height);
}
C.Rectangle.prototype = new C.Element();
C.Rectangle.prototype.constructor = new C.Rectangle();
var usernameLabel = new C.Text('Username:');
usernameLabel.position.set(30,30) // 0,70?
var username = new C.Text('');
username.position.set(0,70) // 0,70
var rect = new C.Rectangle(20,0);
rect.position.set(30,80) // 90,80?
var rect2 = new C.Rectangle(20,0);
rect2.position.set(90,80) // 90,80
From the looks of it, you are declaring position as a 'static' variable on the object, which means it will change. To make it change only on a specific object you need one of the following:
C.Element.prototype.position = new JC.Vector();
or inside a function within the object
this.position = new JC.Vector();
These declarations are for items that are specific to the object, where as the C.Element.position declaration is for something that will be the same in all instances of the object.
Update
Instead of declaring C.Text.prototype = new C.Element(). Try using C.Text.prototype = C.Element.prototype. Hopefully that will fix your problem. Instead of creating a new object to base it on, it bases it directly on the prototype of C.Element
I found the answer! Thanks for the help! The solution was to make the parent object do a call
for a reason I don't fully understand.
C.Text = function(string)
{
C.Object.call(this)
this.string = string || '';
return this;
}
C.Text.prototype = new C.Object();
C.Text.prototype.constructor = C.Text;
Related
So I have a website that does a few things in webgl w/ three.js, and I noticed that loadTexture is going away soon, and that I should probably switch to using textureloaders. Basically, i'd like to preload all my textures before any code executes at the start of my code. I was thinking about doing something like below, which will allow me to load any number of images, and assign them to a texture object that can be called when needed.
var loadedtex = {}
var to_load = ['images/a.png','images/b.png','images/c.png']
var texture_loader = new THREE.TextureLoader();
for(var i=0;i<to_load.length;i++){
texture_loader.load(to_load[i],function(tex){
loadedtex[to_load[i]] = tex
})
}
I'm probably missing something obvious, but I can't quite get it to work correctly. When I use the code above, loadedtex will fill up loadedtex['images/c.png'] every time, instead of properly looping and remember which "i" value it should be using.
Proper result should be something like:
loadedtex['images/a.png'] = Three.Texture{}...
loadedtex['images/b.png'] = Three.Texture{}...
loadedtex['images/c.png'] = Three.Texture{}...
but what i really get is:
loadedtex['images/c.png'] = Three.Texture{}
I know its because the "i" variable will always be max value by the time anything loads, but i'm not sure how to accomplish what i want.
Thanks.
---edit---
This is what I ended up with. Seems to work well. Thanks for the advice.
var loadedtex = {}
var textureloaded = 0
var to_load = ['images/a.png','images/b.png','images/c.png']
var texture_loader = new THREE.TextureLoader();
load_textures = function(){
if (textureloaded == to_load.length){return}
var texture = to_load[textureloaded]
texture_loader.load(texture,function(tex){
loadedtex[texture] = tex
textureloaded += 1
load_textures()
})
}
load_textures()
If you're using ES6:
let assets = [
{ name:'img1', url:'img/img1.png' },
{ name:'img2', url:'img/img2.png' },
{ name:'img3', url:'img/img3.png' },
];
this.textures = {};
for ( let img of assets ) {
this.loader.load(img.url, ( texture ) => {
this.textures[img.name] = texture;
assets.splice( assets.indexOf(img), 1);
if ( !assets.length ) {
this.init()
}
console.log('[TextureLoader] Loaded %o', img.name);
});
}
Try to make a closure function for it like,
for(var i=0;i<to_load.length;i++){
(function(tl){
texture_loader.load(tl,function(tex){
loadedtex[tl] = tex
})
})(to_load[i]);
}
I have a card game, and cards are represented by Javascript objects that are created as instances of class (card > card-type > card-instance). I did it like this so that the cards can share methods.
Then I construct the HTML, and the cards suppose to be able to do all kinds of stuff, like move or attack for example.
move is defined in Card.prototype.move = function... and attack is UnitCard.prototype.attack
and now I am trying to connect the Card objects to their corresponding HTML elements, so that I will be able to so something like
$('#board').on('click', '.card', function (e) {
this.move(this.location, newLocation);
});
An idea I had is to make all the data and functions of the cards part of the DOM, and insert an object somewhere along the prototype chain of the DOM elements, so that the HTML of that card will have a move function. I know this idea is a bit crazy, but I am trying to avoid constant lookups inside objects (find the clicked card by name in the array of all cards and then if other cards that have influence on the action find them in the dom and then find them in the object etc...)
Any suggestions on how to solve this issue?
UPDATE - Current Code:
var Card = function (type, name, epoch) {
var cardHtml = document.createElement('div');
cardHtml.className += "card";
cardHtml.id = name;
cardHtml.cardType = type;
cardHtml.cardName = name;
cardHtml.cardEpoch = epoch;
this.cardHtml = cardHtml;
}
var Agent = function (cardProps, subtype, description, strike, shield, price) {
//fixed
Card.apply(this, cardProps);
this.subtype = subtype;
this.price = price; //agenda
//changable
this.cardHtml.innerHTML = ss.tmpl['cards-agent'].render({
name: this.name,
});
this.cardHtml.strike = strike;
this.cardHtml.shield = shield;
this.cardHtml.location = []; //board/hand/deck/grveyard
}
Agent.prototype = Object.create(Card.prototype);
Agent.prototype.move = function (currentLocation, newLocarion) {
console.log('move');
}
Store a reference to the instance on the element's data object.
var Card = function (type, name, epoch) {
var cardHtml = document.createElement('div');
cardHtml.className += "card";
cardHtml.id = name;
cardHtml.cardType = type;
cardHtml.cardName = name;
cardHtml.cardEpoch = epoch;
this.cardHtml = cardHtml;
$(cardHtml).data("card",this);
}
Now you can access it within events as needed.
$('#board').on('click', '.card', function (e) {
var card = $(this).data('card');
card.move(card.location, newLocation);
});
This of course assumes you can use jquery, per the jquery you're using in your question.
I can think of two additional options.
You could use bind to create a click handler in which this is actually your object instead of the dom element.
el.onclick = (function(){/* ... */}).bind(yourObj);
In this case, within your function, this would be your object instead of the dom element. As long as your object stores a reference to the dom element itelf, then you're set.
Another option would be to define the click handler within a closure which has a variable containing the object.
function bindHanlder(yourObj, el){
el.onclick = function(){
// "yourObj" can be used here.
};
}
I assume that your board has specific places where your cards can be placed.
You should have a Board object containing an array of Places, something like:
function Card(divId){
this.divId = divId;
//generate Card html elements with jquery
}
function Place(divId){
var currentCard = null;
//attach this javascript object to an html element
this.divId = divId;
//register events of this Place where this Place has this.divId
/*
$(document).on("click", this.divId, function(){
});
*/
this.setCard = function(card){
currentCard = card;
//use jquery to add Card html elements to this Place html element
}
}
function Board(){
var places= new Array();
this.addPlace = function(place){
places.push(place);
}
this.moveCard = function(card, toPlace){
toPlace.setCard(card);
}
}
var card1 = new Card("#divCard1");
var card2 = new Card("#divCard2");
var place1 = new Place("#divPlace1");
var place2 = new Place("#divPlace2");
var board = new Board();
board.addPlace(place1);
board.addPlace(place2);
board.moveCards(card1,place1);
This is really off the top of my head. I don't even know if it runs or not. It's just to give you an idea. Interpret it as pseudo code.
Good luck!
Is there a way to get the count of the currently existing textures, buffers or shaders of a WebGL context? Like the numbers you can get in Firefox if you look at about:memory.
I'd like to check if all these are deleted successfully when my application closes.
There is no way to get that info directly from the WebGLRenderingContext but you could easily augment the context yourself something like
var numTextures = 0;
var originalCreateTextureFn = gl.createTexture;
var originalDeleteTextureFn = gl.deleteTexture;
gl.createTexture = function() {
++numTextures;
return originalCreateTextureFn.call(gl);
};
gl.deleteTexture = function(texture) {
--numTextures;
originalDeleteTextureFn.call(gl, texture);
};
You can write similar functions for other resources.
Of course if you want to be perfect you'd probably need to add a flag to each object just incase you try to delete something twice and also check the object passed in is actually the right kind. Something like
var numTextures = 0;
var originalCreateTextureFn = gl.createTexture;
var originalDeleteTextureFn = gl.deleteTexture;
gl.createTexture = function() {
++numTextures;
var texture = originalCreateTextureFn.call(gl);
texture.__deleted__ = false;
};
gl.deleteTexture = function(texture) {
if (texture && texture instanceof WebGLTexture && !texture.__deleted__) {
texture.__deleted__ = true;
--numTextures;
}
originalDeleteTextureFn.call(gl, texture);
};
I'm trying to translate a PHP class into JavaScript. The only thing I'm having trouble with is getting an item out of an array variable. I've created a simple jsfiddle here. I cannot figure out why it won't work.
(EDIT: I updated this code to better reflect what I'm doing. Sorry for the previous mistake.)
function tattooEightBall() {
this.subjects = ['a bear', 'a tiger', 'a sailor'];
this.prediction = make_prediction();
var that = this;
function array_random_pick(somearray) {
//return array[array_rand(array)];
var length = somearray.length;
var random = somearray[Math.floor(Math.random()*somearray.length)];
return random;
}
function make_prediction() {
var prediction = array_random_pick(this.subjects);
return prediction;
}
}
var test = tattooEightBall();
document.write(test.prediction);
Works fine here, you are simple not calling
classname();
After you define the function.
Update
When you make a call to *make_prediction* , this will not be in scope. You are right on the money creating a that variable, use it on *make_prediction* :
var that = this;
this.prediction = make_prediction();
function make_prediction() {
var prediction = ''; //initialize it
prediction = prediction + array_random_pick(that.subjects);
return prediction;
}
You can see a working version here: http://jsfiddle.net/zKcpC/
This is actually pretty complex and I believe someone with more experience in Javascript may be able to clarify the situation.
Edit2: Douglas Crockfords explains it with these words:
By convention, we make a private that variable. This is used to make
the object available to the private methods. This is a workaround for
an error in the ECMAScript Language Specification which causes this to
be set incorrectly for inner functions.
To see the complete article head to: http://javascript.crockford.com/private.html
You never call classname. Seems to be working fine.
Works for me:
(function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
function pickone(somearray) {
var length = somearray.length;
var random = somearray[Math.floor(Math.random()*length)];
return random;
}
var random_item = pickone(this.list);
document.write(random_item);
}());
Were you actually calling the classname function? Note I wrapped your code block in:
([your_code]());
I'm not sure what you're trying to accomplish exactly with the class structure you were using so I made some guesses, but this code works by creating a classname object that has instance data and a pickone method:
function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
this.pickone = function() {
var length = this.list.length;
var random = this.list[Math.floor(Math.random()*length)];
return random;
}
}
var cls = new classname();
var random = cls.pickone();
You can play with it interactively here: http://jsfiddle.net/jfriend00/ReL2h/.
It's working fine for me: http://jsfiddle.net/YznSE/6/ You just didn't call classname(). If you don't call it, nothing will happen ;)
Make it into a self-executing function like this:
(function classname() {
this.list = [];
this.list[0] = "tiger";
this.list[1] = "lion";
this.list[2] = "bear";
function pickone(somearray) {
var length = somearray.length; //<---WHY ISN'T THIS DEFINED??
var random = somearray[Math.floor(Math.random() * length)];
return random;
}
var random_item = pickone(this.list);
document.write(random_item);
})();
var test = tattooEightBall();
document.write(test.prediction);
Should be:
var test = new tattooEightBall(); //forgot new keyword to create object
document.write(test.prediction()); // forgot parens to fire method
and:
this.prediction = make_prediction();
Should be:
this.prediction = make_prediction;
I have been playing a bit with gskinner.com's EaselJS library (http://easeljs.com/, http://easeljs.com/examples/game/game.html), which makes dealing with HTML5's canvas a lot easier.
So I'm trying to remake something like Space Invaders in the canvas. So far it's not much, just the payer moving left to right. See the progress here: http://jansensan.net/experiments/easeljs-space-invader/
For the invaders, I needed an animation, so I followed a tutorial on how to do so: http://www.youtube.com/watch?v=HaJ615V6qLk
Now that is all good and dandy, however I follow gskinner.com's way of creating "classes": http://easeljs.com/examples/game/Ship.js I'm not certain if I can call that a class, but it is used as such.
So below is the class that I wrote for the Invader, however it seems like the BitmapSequence does not seem to be added to EaselJS's stage. Anyone can guide me through this? Thanks!
// REFERENCES
/*
http://easeljs.com/examples/game/Ship.js
*/
// CLASS
(function(window)
{
function Invader()
{
this.initialize();
}
var p = Invader.prototype = new Container();
// CONSTANTS
// VARS
p.image;
p.bitmapSequence;
// CONSTRUCTOR
p.Container_initialize = p.initialize; //unique to avoid overiding base class
p.initialize = function()
{
this.Container_initialize();
this.image = new Image();
this.image.onload = p.imageLoadHandler;
this.image.onerror = p.imageErrorHandler;
this.image.src = "assets/images/invader-spritesheet.png";
}
p.imageLoadHandler = function()
{
var frameData = {
anim:[0, 1, "anim"]
}
var spriteSheet = new SpriteSheet(p.image, 22, 16, frameData);
p.bitmapSequence = new BitmapSequence(spriteSheet);
p.bitmapSequence.regX = p.bitmapSequence.spriteSheet.frameWidth * 0.5;
p.bitmapSequence.regY = p.bitmapSequence.spriteSheet.frameHeight * 0.5;
p.bitmapSequence.gotoAndStop("anim");
p.addChild(p.bitmapSequence);
}
p.imageErrorHandler = function()
{
console.log("Error: the url assets/images/invader-spritesheet.png could not be loaded.");
}
window.Invader = Invader;
}(window));
Does you p.image/this.Container_initalize actually exist at that point? As you switch between using this. and p. between your init and other functions, while they might seem to be the same practice has often taught me its not the case. Try changing your init function to this:
p.initialize = function()
{
p.Container_initialize();
p.image = new Image();
p.image.onload = p.imageLoadHandler;
p.image.onerror = p.imageErrorHandler;
p.image.src = "assets/images/invader-spritesheet.png";
}