problems with animation transparency
krumza

HI!

I think that i try everything, but it no work as i planned.

In my case i have Object and some listeners: onMouseIn and on MouseOut I want that sprite be semi-semi-transparent on hover (.25) and then again non-transparent on mouseOut.(I greatly simplified, because in my example there is a tile on which there are objects that I want to make translucent when you hover the mouse on the tiles)

First i try:

            b.onMouseIn = function() {
                this.getSprite(0).setDrawModifiers([{type: 'alpha', alpha: .2}]);
            }
            b.onMouseOut = function() {
                this.getSprite(0).setDrawModifiers([{type: 'alpha', alpha: 1}]);                    
            }

also i try:

b.onMouseOut = function() {
                this.getSprite(0).setDrawModifiers([]);                    
            }

some problems with onMouse listeners if litle lags detected and this construct do sprite alpha = .2 but not 1 on mouseOut

Then i try:

b.onMouseIn = function() {                       this.getSprite().setDrawFunction(wade.drawFunctions.alpha_(1,this.getSprite().draw));
}
b.onMouseOut = function() {
this.getSprite().setDrawFunction(wade.drawFunctions.alpha_(1,this.getSprite().draw));
}

same effect - sprites set semi-transparent and not back to alpha=1 again

Then i try with Path:

            b.onMouseIn = function() {
                    this.setPath(wade.getPath('fade-out'),0,1);
            }
            b.onMouseOut = function() {
                   this.setPath(wade.getPath('fade-in'),0,1);
            }

But it call some laggy when i move mouse on my gamescreen...

Please help. It seems easy, but i broke my brain ;)

All 21 Comments
Gio

Hi

I think I understand what may be confusing you... the fact is that draw functions are always chained. When you do:

this.getSprite().setDrawFunction(wade.drawFunctions.alpha_(0.2,this.getSprite().draw));

This applies an alpha draw function on top of the Sprite's current draw function. Say that the current draw function is the default draw function, what you get is:

- Set Alpha to 0.2
- Draw the sprite with its default draw function

Then onMouseOut you are doing this:

this.getSprite().setDrawFunction(wade.drawFunctions.alpha_(1,this.getSprite().draw));

What you do there means: "Add a draw function with alpha 1 on top of the current Sprite's draw function". But you had already modified the current draw function. So overall you get:

- Set Alpha to 1
- Set Alpha to 0.2
- Draw the sprite with its default draw function

So maybe what you want to do is:

var originalDrawFunction = b.getSprite().getDrawFunction();

b.onMouseIn = function() 
{
    this.getSprite().setDrawFunction(wade.drawFunctions.alpha_(0.2,originalDrawFunction));
};

b.onMouseOut = function() 
{
    this.getSprite().setDrawFunction(wade.drawFunctions.alpha_(1,originalDrawFunction));
};

However I think that doing this with a Path would look nicer and smoother (not only you change the opacity, but you do so smoothly with some transition). I think that you are experiencing some lag because the current behavior of SceneObject.setPath() is that you set a path, and the object will start following it on the next simulation step. This will change in version 3.6, the SceneObject will start following the Path immediately. But for now you could do this, as a workaround:

b.setPath(myPath);
b.step();

 

krumza

Bigt big Thank you, Gio!

Now it clearly absolutly.

About lag problem - i have some PC, one of them show 50-6(!!!) FPS.

What is my code: it is generated map

 for(var i=0;i<100;i++){
   for(var j=0;j<100;j++){
    var newMarker = wade.getSceneObject("Marker").clone();//layer 16 as ground
    wade.addSceneObject(newMarker,true);

    //...here isertt another objects like houses and trees on layer 15
}
}

total i have ~15000-20000 objects 10000 of them is "Marker" clones. Where "Marker" is:

            var a = new Sprite('images/ground.png',16);//it rhombus 512*256
            var activeMarker = new Sprite('ui/activemarker.png',16);//it rhombus 512*256
            var hoverMarker = new Sprite('images/hovermarker.png',16);//it rhombus 512*256
            var b = new SceneObject(a);
            b.addSprite(activeMarker,0,1);
            b.addSprite(hoverMarker,0,2);
            b.setName('Marker');
            b.setAsTemplate(true);
            wade.addSceneObject(b);
            a.usePixelPerfectMouseEvents(true);
            activeMarker.usePixelPerfectMouseEvents(true);
            hoverMarker.usePixelPerfectMouseEvents(true);
            wade.addEventListener(b, 'onClick');
            wade.addEventListener(b, 'onMouseIn');
            wade.addEventListener(b, 'onMouseOut');

May be descrease of FPS because i use so much listeners with pixelperfect (10000 listeners seems weird , but i dont understand logic of wade ) ?

There is a another way make reactive objects with hover and click listeners without adding listeners for each object, but use global listener of wade?

Gio

I think it is reasonable to assume that having so many event listeners would slow thing down. Especially mouseIn and mouseOut, they are pretty expensive. They have to be pretty expensive, because there are a wide variety of cases that they need to be able to cover (for example some objects may be listening for mouseOut only and not mouseIn, etc.).

However in your particular case you should be able to greatly simplify this, as it seems that you can only ever have one object with a hover state at any one time. You may be able to speed it up even more if you have all your listener sprites on the same layer, and also if you know that those sprites aren't overlapping each other.

In general, when you have so many potential listeners, I would use a different approach: use a function to listen for some global mouse events (for example only onClick and onMouseMove). In that function, get the sprites at the current mouse poisition, then handle the logic for showing / hiding / changing the transparency of your objects without onMouseIn and onMouseOut events.

var currentHoverObject;
wade.app.onMouseMove = function(data)
{
	var worldPosition = wade.screenPositionToWorld(layerId, data.screenPosition);
	var area = {minX: worldPosition.x, maxX: worldPosition.x, minY: worldPosition.y, maxY: worldPosition.y};
	var sprites = wade.getSpritesInArea(area, layerId);
	
	// if you know that there may be  multiple sprites overlapping each other, add some code to get the one that you 
        // care about. Here for simplicity I am assuming that only the first sprite is important
	var sprite = sprites[0];
	if (sprite)
	{
		var obj = sprite.getSceneObject();
		if (obj != currentHoverObject)
		{
			if (currentHoverObject)
			{
				currentHoverObject.onMouseOut();
			}			
			obj.onMouseIn();
		}
	}
	currentHoverObject = obj;
};

Having said that, if you are experiencing poor performance, it's always a good idea to run a profile to see what the actual cause is. Do not guess, even if I think that in this case it was a good guess :)

krumza

i sad because profiler for me as a pink unicorn ^)

Last question in this:

i have a big big city - code where for(100)  - it only one district, of hundreds,

For now - i generate it procedural,  

But actually i will get this array from server (server part yet allready complete).

And all new districts, loaded from server will be added  on canvas to drawing districts

Question is - i have a thousands objects with "onclick" listeners, or behaviors with "onclick" listeners - maybe good idea  - rewrite "onclick" too as you show me in previous post?

Gio

Yes, I think that's a good idea. To be clear, that's roughly what wade.iso already does... but I know you don't want to use that :)  So yeah, perhaps write something similar for onClick. In fact, it'd be even simpler for onClick.

krumza

Big big mistery

it seems not easy.Ok i have an object:

var a = new Sprite('images/ground.png',16);//it rhombus 512*256
//bla bla bla
wade.addSceneObject(b);
a.usePixelPerfectMouseEvents(true);
activeMarker.usePixelPerfectMouseEvents(true);
hoverMarker.usePixelPerfectMouseEvents(true);
wade.addEventListener(b, 'onClick');
wade.addEventListener(b, 'onMouseIn');
wade.addEventListener(b, 'onMouseOut');

next step i  coment this lines:

wade.addEventListener(b, 'onClick');
//wade.addEventListener(b, 'onMouseIn');
//wade.addEventListener(b, 'onMouseOut');

and uncomment this:

b.onMouseIn = function() 
{
//    bla
};

b.onMouseOut = function() 
{
//bla bla
};

and in wade.app.onMouseMove insert your code

it work not as i expect (because i have intersected rhombus sprites and it code call erron on objects without onMouseIn function) and i commented your code in app.onMouseMove,

but hover - effect is working! until i comment onMouseIn and Uot functions for object

/*
b.onMouseIn = function() 
{
//    bla
};

b.onMouseOut = function() 
{
//bla bla
};
*/

How it work without wade.addEventListener(b, 'onMouseIn') ???

And how it do with sprite with intersect and irregular form:

p.s. i still try do it without iso plugin

Gio

How it work without wade.addEventListener(b, 'onMouseIn')

When you call wade.addSceneObject(b), WADE will look in your object and all its behaviors to see if it's got any functions that match event names. If b.onMouseIn is defined, it will automatically listen for onMouseIn. You don't need to call wade.addEventListener() explicitly.

If you want you can do wade.addSceneObject(b, false) and WADE will NOT look for functions that match event names. In that case, you will need to listen for events explicitly.

If you have multiple sprites on top of each other, like I said in my code comment above, you may want to find the right ones to catch mouse events. Perhaps add an isRhombus flag on the relevant sprites, or something along those lines.

krumza

How add functions when number of objects are hundreds? As i understand  use Behavior is equal listenrers ?

Will i do wade.addSceneObject(a, false),a,onMouseIn(){{},wade.addSceneObject(b, false),b,onMouseIn(){{},wade.addSceneObject(c, false)c,onMouseIn(){{}...

Or add behaviour to each object with false autolisten will not use event without my call??

P.S. honestly I'm not yet annoying you with my questions?

Gio

Don't worry I only answer questions on the forum when I need a break from coding, so it's no bother :)

If you have lots of objects that need to behave in the same way, using the same Behavior for all of them is probably the better solution. Among other things, this allows you to change code in one place and have it reflected everywhere.

It doesn't matter if the onMouseIn function is defined for your object or for your behavior: if you call wade.addSceneObject(b), it will automatically listen for onMouseIn, even if it's only defined in the Behavior and not on the object itself. You would still need to call wade.addSceneObject(b, false) if you don't want the object to listen for onMouseIn. Alternatively, you could rename the onMouseIn  function and use some other name that does not match an event name.

krumza

My desire seems impossible...

Hi, Gio, i think that impossible.

I again teel less about this gameplay: there is 2 layers of objects, whitch each player can interract - ground layer (player can buy piece of city) and building layer (player can build new and interract with existing building such as attack, defend upgrade etc.)

According you decision i remove all eventlisteners from objects and leave only global eventlisteners on click, onmove etc.

Now i check every mouseMove whitch rhombus over mouse (i see every point as a couple of lines and if it in rhombus - rhombus is hovered, less math but look like pixelperfect)

Ok it work very nice with rhombus - because it have a regular form and spaces.

But buildings... Very irregular and intersected - so simple math no working,

I go on the way - when i create map - i make an array for ground objects. As example: 

var newMarker = wade.getSceneObject("Marker").clone();  //ground tile
newMarker.setPosition(RowWidth*j,RowHeight*i/2);
newMarker.cellX = dataCoordsx;
newMarker.cellY = dataCoordsy;
newMarker.cellXY = dataCoordsx+"/"+dataCoordsy;
newMarker.containobj = [];//array for all building on this area
newMarker.price = 10000;
newMarker.owner = "Mr. Smith";
wade.addSceneObject(newMarker,false);

//insert random building on map

var newShop = wade.getSceneObject(get_random(list)).clone();  
newShop.setPosition(RowWidth*j,RowHeight*i/2);
if(newShop.getSprite(1)){
   newShop.getSprite(1).setVisible(false);   
}                            
wade.addSceneObject(newShop,false); 
newShop.addBehavior(BuildingBehavior);
newMarker.containobj.push(newShop);//insert it in array of mother tile 

This way help me "shoot two rabbits per one shot" - i know whitch building is hovered - only knew whitch tile is hovered.

But it look not like pixelperfect...

I see that in flash games it work fine - flash is so much better???

And so pure performance too also when i want change opacity for buildings under chosen.

I stalled.

I add a car that move and this one moving car go slow and partial move.

I don't know the reason maybe the fact that too many objects on the map? but then there will be more requests to the server (if map will be less in N times there are  N power 2 request to server)

In which direction to move to optimize?
Reducing the size of the map doesn't give you much improvement.
Can be the size of the images too big?
Maybe it's worth to combine them into a single sprite sheet? Maybe  i must descrease UI elemets with listeners (although I haven't even finished it)?

Please see it  here link 

i recreate this on Phaser and result is weird - ypu can see if you want here  (and this is the reason beacause i falling in love to WADE :))))

 

krumza

I try this^

//first method seems faster
var hoverromb = [];
var screenArea = {};
screenArea.minX = screenArea.maxX = eventData.screenPosition.x;
screenArea.minY = screenArea.maxY = eventData.screenPosition.y;
var sprites = wade.getSpritesInScreenArea(screenArea);                    
for(var j=0;j<sprites.length;j++){
  if(sprites[j].getSceneObject().cellXY == nPos.H+"/"+nPos.V){
     hoverromb.push(sprites[j].getSceneObject());
     break; 
 }
} 

//second method
                    
var hoverromb = wade.getSceneObjects("cellXY",nPos.H+"/"+nPos.V);

first method  seems faster

Gio

Nothing is impossible :)

Surely you only need to optimise it a bit. From the screenshot of your profile above I can see that you are spending a lot (A LOT!) of time in wade.getSceneObjects(). Perhaps you can avoid this call? It must be possible to avoid it and do something faster instead

krumza

But how get object under mouse cursor without wade.getSceneObjects()?

And why method 1 in previous post seems working faster?

i try to remove/reduce wade.getSceneobjects() - but how how make an interactive objects

Next step -  porfornance discreased by wade.setLayerSorting(this.gamelayers.GAME_OBJ_LAYER,'bottomToTop');

I commented this - but now as you understand - cars move above buildings etc.

How use sorting without less perfonmance?

Gio

I don't understand why you need wade.getSceneObjects()... the wade.app.onMouseMove function that I posted above does not use it.

wade.getSceneObjects(property, value) iterates over ALL the objects in the scene, which in your case is a lot of objects. This explains why your second method is much slower, I believe.

The wade.iso module, instead, maintains a 2d array of all the objects in each cell, and as such it's much faster at getting the objects in a specific cell. You could do the same. Or have a "cellXY" object that contains one array per cell. But again, it seems that you will end up rewriting what wade.iso does... wouldn't it be quicker to just use that?

Sorting is (I think) as quick as it can be. It only sorts objects that may have moved, and anything that is near the moving objects, deciding which sorting algorithm to use depending on the actual numbers. If you want to have such a large world, you should stop objects that are far away from the player from moving (and playing animations and all that). Only update their positions and animations when the player is getting close, otherwise it's going to do a lot of calculations for nothing.

Perhaps you could just remove far away objects from them scene, and re-add them when they're closer to the player. They will still be in memory, just not part of the scene, so they won't affect sorting and won't process events, speeding everything up.

krumza

Please do not take offense at me,
Just explain that I started with isometric plugin but it is not enough memory in Google chrome if you do a map of 100*100, then the infinite scrolling mechanism with isometric plugin too, in my opinion, would have memory consumption if  i will  sometimes add tiles with arrays of 100*100.
Of course there are a huge number of optimizations, which I still have not figured out yet.
But I think the most sensible way to do map-type infinite scrolling - all that is outside of the camera is removed, all the movement is added from the array map of the world.
Thus in the visible region ( and indeed in the mountains WADE) will be only a limited number of objects. And i can safely hang already event handlers to objects such as mouse movement, hovering the mouse etc.

But the question arises - how to control the visible area?
While I'm only using the function for mouse movement ( when the player scrolled map) and when the mouse wheel changes the zoom in this point I create a zone terminator (as the lunar terminator(solar)) and remove all objects which it touches:

if(dirHorz=="right"){
 terminator.minX = terminator.maxX = wade.screenPositionToWorld(16, {x:- wade.getScreenWidth()/2- RowWidth/cameraPos.z,y:0}).x;
 terminator.minY = wade.screenPositionToWorld(16, {x:0,y:-wade.getScreenHeight()/2}).y;
 terminator.maxY = wade.screenPositionToWorld(16, {x:0,y:wade.getScreenHeight()/2}).y;                            
 var SpritesToTerminateOnLeft1 = wade.getSpritesInArea(terminator,16);                        
 for(var j=0;j<SpritesToTerminateOnLeft1.length;j++){
   if(SpritesToTerminateOnLeft1[j].getSceneObject().containobj&&SpritesToTerminateOnLeft1[j].getSceneObject().containobj.length>0){
   for(var l=0;l<SpritesToTerminateOnLeft1[j].getSceneObject().containobj.length;l++) {                                                wade.removeSceneObject(SpritesToTerminateOnLeft1[j].getSceneObject().containobj[l]);
}
}                                wade.removeSceneObject(SpritesToTerminateOnLeft1[j].getSceneObject())
}
}

And now I wonder - delete object - removes all event handlers of this object? Enough wade.removeSceneObject(object) without removeEventListener
(sceneObject,event)?
Logically everything seems to be automatically removed by the JavaScript garbage collector. But maybe it collected in some array of WADE

In General, two questions for you - is it enough to just delete the object?
And the question is how to track change visible area (camera view).
Or here are my terminators in hand to hang only on the function associated with changes in the visible region such as a movement or zoom(mouseMove and Scroll)

krumza

Several problems:

1: manipulation with coordinates in different layers such as^

wade.screenPositionToWorld(16, {x:0,y:-wade.getScreenHeight()/2}).y;

return not fixed values - it return as example: 1234,5678910 

Nothing serious - it treated by .toFixed():

var layerrightside = wade.screenPositionToWorld(16, {x:wade.getScreenWidth()/2+RowWidth/cameraPos.z,y:0}).x;
BorderRows.Right = +(layerrightside/256).toFixed();

but the nature of the effect is unclear

2. i use a watch plifyll for detect an change BorderRows.Right:

BorderRows.watch("Right", function (id, oldval, newval) {//function to insert new row}

There is a WADE method for watching at variables?

3. My "terminator" delete all objects on layer 16,(previous my post)

But my templates on 16 layer too! and terminator delete All my templates

How i can defend my template object?

I found but is so hard to read...^

terminator.minX = terminator.maxX = wade.screenPositionToWorld(16, {x:- wade.getScreenWidth()/2-RowWidth/cameraPos.z,y:0}).x;
terminator.minY = wade.screenPositionToWorld(16, {x:0,y:-wade.getScreenHeight()/2}).y;
terminator.maxY = wade.screenPositionToWorld(16, {x:0,y:wade.getScreenHeight()/2}).y;                            
var SpritesToTerminateOnLeft1 = wade.getSpritesInArea(terminator,16);                        
for(var j=0;j<SpritesToTerminateOnLeft1.length;j++){                                if(SpritesToTerminateOnLeft1[j].getSceneObject().containobj&&SpritesToTerminateOnLeft1[j].getSceneObject().containobj.length>0){
  for(var l=0;l<SpritesToTerminateOnLeft1[j].getSceneObject().containobj.length;l++) {                                         wade.removeSceneObject(SpritesToTerminateOnLeft1[j].getSceneObject().containobj[l]);
  }
}
if(!(SpritesToTerminateOnLeft1[j].getSceneObject().isTemplate())){                                    wade.removeSceneObject(SpritesToTerminateOnLeft1[j].getSceneObject())   
}
}

really it is impossible is easier?

Gio

Hi

Several questions, I'll try to cover all of them in order

> is it enough to just delete the object

If you use wade.removeSceneObject or, if using wade.iso (wade.iso.deleteObject) that should remove ALL the references that wade if keeping for that object. However this won't release any images / textures associated with that object. If you don't need an asset in memory anymore, there are a bunch of functions such as wade.unloadImage that might be useful to you.

> how to track change visible area

To get the current screen area in a layer's coordinate space:

var halfWidth = wade.getScreenWidth() / 2;
var halgHeight = wade.getScreenHeight() / 2;
var area = {minX: -halfWidth, minY: -halfHeight, maxX: halfWidth, maxY: halfHeight};
area = wade.screenBoxToWorld(layerId, area);

For version 3.6 (hopefully to be released this week) we are introducing an onCameraMove event that will fire every time the camera position or zoom level changes. You would just add an onCameraMove function in your App object to handle the logic.

Right now, as a workaround, you'd have to set a main loop with wade.setMainLoop() to monitor camera position changes.

> manipulation with coordinates return not fixed values

I am not sure why you need a fixed value?

> There is a WADE method for watching at variables

No, and I think this would be good to add in a future version. There is the JavaScript way that is fairly simple though, and supported pretty much everywhere.

> How i can defend my template object?

if (!mySceneObject.isTemplate())
{
    wade.removeSceneObject(mySceneObject);
}

 

krumza

I don't even know more to enjoy from the new version or be upset by the fact that almost everything made before its release :)

Thank you for answer!

krumza

Some question again

> manipulation with coordinates return not fixed values

wade.screenPositionToWorld(16, {x:wade.getScreenWidth()/2+RowWidth/cameraPos.z,y:0}).x;

sorry I didn't mean fixed and mean not whole numbers, but fractional numbers (I'm not sure what is normally translated into English)
I expect to see a coordinate of x=1234 but the function returns 1234,56789

- there's also the problem with deleting objects
I take a 2 line of height two screen on left and right  and another lines with width two screen on bottom and up and remove all objects which come across it.
At the same time, I've  rendering new objects when the player scroll to right.
And for some reason, not always the objects are removed and the farther we scrollin right the more is. I would like to understand why

Gio

I understand what you mean by non-fixed values / fractional numbers. However there is no reason why world coordinates should be integer numbers. Normally it makes sense for screen coordinates to be integers, because you have a finite number of pixels on your screen.

However, world units are, in principle, continuous coordinates. It's perfectly valid for something to be at position (0.5, 123.45) in the world for example. Depending on the camera position and zoom level, that will end up being at various positions on your screen.

> And for some reason, not always the objects are removed and the farther we scrollin right the more is. I would like to understand why

It's difficult to tell without looking at your code, but I suspect the objects are not being removed at the right time. Are you doing this in a main loop, i.e. using wade.setMainLoop() ?

 

krumza

Hi

 - about fixed values - i am understand, but it would be more convenient check coordinates as if(!(coords.x%tilewidth)){//got row}

 - about second: i think that is more expensive use getSpritesInArea on every update().

Now in my code trigger to delete - is mouseMove

but i recreating this mechanism to next way:

App = function() {
  var VISIBLEZONE = {
     Right:0,
     Left:0,
     Bottom:0,
     Top:0
  };
  //use watch.js to watch visible zone https://github.com/melanke/Watch.JS
  watch(VISIBLEZONE, ["Right","Left","Bottom","Top"], function(data){
     if (data=="right"){
       wade.app.onRightMove();
     }  
  });

  this.init = function() {
    //...
    wade.setMainLoop(wade.app.Update);
    //...
  }

  //...

  this.Update= function(){
    var layerrightside = wade.screenPositionToWorld(16,{x:wade.getScreenWidth()/2+RowWidth/wade.getCameraPosition().z,y:0}).x;
    var layerleftside = wade.screenPositionToWorld(16, {x:-wade.getScreenWidth()/2-RowWidth/wade.getCameraPosition().z,y:0}).x;
    var toprightpoint = wade.screenPositionToWorld(16, {x:0,y:-wade.getScreenHeight()/2-RowHeight/wade.getCameraPosition().z}).y;
    var bottomrightpoint = wade.screenPositionToWorld(16, {x:0,y:wade.getScreenHeight()/2+RowHeight/wade.getCameraPosition().z}).y;

    VISIBLEZONE.Right = +(layerrightside/256).toFixed();
    VISIBLEZONE.Left = +(layerleftside/256).toFixed();
    VISIBLEZONE.Top = +(toprightpoint/128).toFixed();
    VISIBLEZONE.Bottom = +(bottomrightpoint/128).toFixed();
   }
   
   this.onRightMove = function(){
     //create objects on right side
     //delete objects on left side
   }
}

had to use the library watch.js for the reason that I'm not getter/setter/OOP ninja

Post a reply
Add Attachment
Submit Reply
Login to Reply