How do I HitTest two rotating objects properly? (A way to avoid bounding boxes) - actionscript-3

I took an intro level flash course in college this semester and our final task was to make a mini-flash game.
I had to make a pipe-dream type game where there are a number of levels, and in each level you have to align the pipes so that the water flows and then you can pass to the next level.
I successfully made the first level, but upon making the second level,
where I placed a lot of curved pipes (by curved pipes I mean the attached image: ![Curved Pipe]: (http://imgur.com/mwpXAMn) )
I discovered that the method I use to decide when a level is complete is not working properly.
I was using HitTestObject, basically, I was testing whether 2 objects, Pipe_1, and Pipe_2, were intersecting. If all pipes intersected in the correct way, then procession to the next level is granted.
The problem with this I discovered is that flash has bounding boxes for movie clips you make, and that HitestObject uses bounding boxes to test for hits. Therefore, when you rotate a leftpipe so that it does not touch a straight pipe on screen, the bounding boxes still touch and it returns "collision" when in fact it is not actually touching on screen.
I looked up and found that you can use HitTestPoint but I can't figure out how to somehow make dynamic variables (that change upon rotation of object) that store one or two specific points on the leftpipe, say the two ends of it.
Once If I figure out how to get these values into a variable correctly, then I can figure out how to do HitTestpoint.
Also, I know of the LocaltoGlobal function but no matter what I try it keeps coming up with:
"Scene 1, Layer 'Layer 1', Frame 1, Line 30 1118: Implicit coercion of a value with static type Object to a possibly unrelated type flash.geom:Point."
meaning I don't know the correct code to store an x and a y coordinate as dynamic variables.
edit: ok since a person asked, I hunted this piece of code off the web and this is the one I was trying to play around with but to no avail:
How to use HitTest for 2 rectangles, r1 is rectangle 1 r2 is rectangle 2.
var r1width:Number = 135.0; //width of retangle 1 whith rotation 0
var r2width:Number = 93.0; //width of retangle 2 whith rotation 0
var p1:Object = {x:(r1width/2), y:(r1width/2)};
var p2:Object = {x:(-r1width/2), y:(r1width/2)};
var p3:Object = {x:(-r1width/2), y:(-r1width/2)};
var p4:Object = {x:(r1width/2), y:(-r1width/2)};
r1.localToGlobal(p1);
r1.localToGlobal(p2);
r1.localToGlobal(p3);
r1.localToGlobal(p4);
var p5:Object = {x:(r2width/2), y:(r2width/2)};
var p6:Object = {x:(-r2width/2), y:(r2width/2)};
var p7:Object = {x:(-r2width/2), y:(-r2width/2)};
var p8:Object = {x:(r2width/2), y:(-r2width/2)};
r2.localToGlobal(p5);
r2.localToGlobal(p6);
r2.localToGlobal(p7);
r2.localToGlobal(p8);
if((r2.hitTest(p1.x, p1.y, true))||(r2.hitTest(p2.x, p2.y, true))||(r2.hitTest(p3.x,
p3.y, true))||(r2.hitTest(p4.x, p4.y, true)))
{
trace('collision');
}
if((r1.hitTest(p5.x, p5.y, true))||(r1.hitTest(p6.x, p6.y, true))||(r1.hitTest(p7.x,
p7.y, true))||(r1.hitTest(p8.x, p8.y, true)))
{
trace('collision');
}
I did not write this code and it does not work. I'm not sure what "Object" is because I've never used it before, I'm assuming in this case it's sort of acting like a coordinate pair.
Also, this code is to hittest 2 rectangles, whereas I'm using an L-shaped pipe, so the x/y calculation would be quite different I imagine.
This code above gives the same error that I posted before:
Implicit coercion of a value with static type Object to a possibly unrelated type flash.geom:Point.
and it gives it first on line r1.localToGlobal(p1);

Instead of using Object, you need to use Point like so:
var p1:Point = new Point(r1width/2, r1width/2);

Related

Jumping back and forth inside a Movieclip with a button?

So, I'm very much a beginner in AS3. I've been reading and figuring out things as I go, though I can't wrap my head around this.
So, I have 4 frames. Each frame has a different movie clip, MC1,MC2,MC3,MC4
Inside those four movie clips, there is another movie clip with the same instance name for each: BC, and inside that movie clip there are two frames. Frame 1 has a dot, and frame 2 does not.
MC1>BC>(2 frames)
MC2>BC>(2 frames)
and so on....
What I'm trying to do: I wanted to see if there was any way to control the frame navigation of BC inside all four MC clips at the same time with one button.
I want to switch back and fourth between the two frames inside the BC movie clip.
I'm at a loss, I've tried quite a few things.
You should be able to do so by giving them all the same instance name (so long as there is only ever one of them on screen at once).
So lets say you have a button that spans all 4 frames with an instance name of navBtn and you gave each of the MC1-4 clips the same instance name of MC. You could do the following on frame 1:
navBtn.addEventListener(MouseEvent.CLICK, navBtnClick);
function navBtnClick(e:Event):void {
if(MC.BC.currentFrame == 2){
MC.BC.gotoAndStop(1);
}else{
MC.BC.gotoAndStop(2);
}
}
Reading your question again, perhaps what are looking for is to have each clip automatically go to the same frame for their BC child when they load? If that is the case, then follow the example in the comment on your question by #Organis. Here is one way you could accomplish this:
Create two variable on frame one of your main timeline:
var BC_NAV_CHANGE:String = "BC_NAV_CHANGE";
var BC_CurFrame:int = 1;
Then, when you need to change the frame of the BC objects, do the following:
//create a function that you call when you want to change the BC frame
function toggleBCFrame(e:Event = null){
MovieClip(root).BC_CurFrame = MovieClip(root).BC_CurFrame == 1 ? 2 : 1;
//the line above is a if/else shorthand, that is setting a new value to the `BC_CurFrame` var,
//if the current value is `1`, it will set it to `2`, otherwise it will set it to `1`
MovieClip(root).dispatchEvent(new Event(MovieClip(root).BC_NAV_CHANGE));
//this line (above) dispatches a event telling anything that's listening that the variable has changed
}
If the code above is on the main timeline, you can forgo all the MovieClip(root). parts of the code.
Now, on the timeline of your BC MovieClip(s), put the following code:
//create a function that goes to and stops at the frame stored in the global variable
function updateFrame(e:Event = null){
gotoAndStop(MovieClip(root).BC_CurFrame);
}
//next listen for the BC_NAV_CHANGE event, and call the above update function above any time that event happens
MovieClip(root).addEventListener(MovieClip(root).BC_NAV_CHANGE, updateFrame);
//lastly, call the update function right away so when the BC clips loads it immediately goes to the correct frame
updateFrame();

Best and most performant implementation of dynamic shapes in cesium

I am currently working an application that is using a Cesium Viewer. I need to be able to display a collection of shapes that will be updated dynamically. I am having trouble understanding the best way to do this.
I currently am using Entities and using CallbackProperties to allow for the updating of shapes.
You can through this into a sandcastle to get an idea of how I am doing this. There is a polygon object that is being used as the basis for the cesiumCallback, and it is getting edited by another piece of code. (simulated with the setTimeout)
var viewer = new Cesium.Viewer('cesiumContainer', {});
var polygon = {};
polygon.coordinates = [
{longitude: 0, latitude: 0, altitude: 0},
{longitude: 10, latitude: 10, altitude: 0},
{longitude: 10, latitude: 0, altitude: 0}
];
// converts generic style options to cesium one (aka color -> material)
var polOpts = {};
// function for getting location
polOpts.hierarchy = new Cesium.CallbackProperty(function() {
var hierarchy = [];
for (var i = 0; i < polygon.coordinates.length; i++) {
var coordinate = polygon.coordinates[i];
hierarchy.push(Cesium.Cartesian3.fromDegrees(coordinate.longitude, coordinate.latitude, coordinate.altitude));
}
return hierarchy;
}, false);
viewer.entities.add({polygon: polOpts});
setInterval(function(polygon){
polygon.coordinates[0].longitude--;
}.bind(this, polygon), 1000);
The polygon being passed in is a class that generically describes a polygon, so it has an array of coordinates and style options, as well as a render method that calls this method renderPolygon passing in itself.
This method of rendering shapes works for everything I need it to, but it is not very performant. There are two cases for shapes updating, one type of shape will be updated over a long period of time, as a slow rate like once every few seconds. The other is shapes that will will get updated many times, like thousands, in a few seconds, then not change again for a long time, if ever.
I had two ideas for how to fix this.
Idea 1:
Have two methods, a renderDynamicPolygon and a renderStaticPolygon.
The renderDynamicPolygon method would do the above functionality, using the cesiumCallbackProperties. This would be used for shapes that are getting updated many times during the short time they are being updated.
The renderStaticPolygon method would replace the entities properties that are using callbackProperties with constant values, once the updating is done.
This creates a lot of other work to make sure shapes are in the right state, and doesn't help the shapes that are being updated slowly over a long period of time.
Idea 2:
Similarly to how the primitives work, I tried removing the old entity and adding it again with its updated properties each time its need to be updated, but this resulted in flickering, and unlike primitives, i could not find a async property for entities.
I also tried using primitives. It worked great for polylines, I would simply remove the old one and add a new one with the updated properties. I was also using the async = false to ensure there was no flickering. This issue I ran into here was not all shapes can be created using primitives. (Is this true?)
The other thing I tried was using the geometry instance using the geometry and appearance. After going through the tutorial on the cesium website I was able to render a few shapes, and could update the appearance, but found it close to impossible to figure out how to update the shapes correctly, and also have a very hard time getting them to look correct. Shapes need to have the right shape, a fill color and opacity and a stroke color, opacity and weight. I tried to use the polygonOutlineGeometry, but had not luck.
What would be the best way to implement this? Are one of these options headed the right way or is there some other method of doing this I have not uncovered yet?
[Edit] I added an answer of where I have gotten, but still not complete and looking for answers.
I have came up with a pretty good solution to this, but it still has one small issue.
I made too ways of showing entities. I am calling one render and one paint. Render uses the the Cesium.CallbackProperty with the isConstant property true, and paint with the isConstantProperty false.
Then I created a function to change the an entity from render to paint and vice vera. It goes through the entities callback properties an uses the setCallback property to overwrite the property with a the correct function and isConstant value.
Example:
I create a ellipse based on a circle object I have defined.
// isConst is True if it is being "painted" and false if it is being "rendered"
ellipse: lenz.util.extend(this._getStyleOptions(circle), {
semiMinorAxis: new Cesium.CallbackProperty(
this._getRadius.bind(this, circle),
isConst
),
semiMajorAxis: new Cesium.CallbackProperty(
this._getRadius.bind(this, circle),
isConst
),
})
So when the shape is being updated (while the user is drawing a shape) the shape is rendered with the isConstant being false.
Then when the drawing is complete it is converted to the painted version using some code like this:
existingEntity.ellipse.semiMinorAxis.setCallback(
this._getRadius.bind(this, circle),
isConst
);
existingEntity.ellipse.semiMajorAxis.setCallback(
this._getRadius.bind(this, circle, 1),
isConst
);
This works great performance wise. I am able to draw hundreds of shapes without the frame dropping much at all. I have attached a screen shot of the cesium map with 612 entities before and after my changes, the frame rate is in the upper right using the chrome render tool.
Before: Locked up at fps 0.9
Note: I redacted the rest of the ui, witch makes the globe look cut off, sorry
And after the changes: The fps remains at 59.9, almost perfect!
Whenever the entity is 'converted' from using constant to not constant callback properties, it and all other entities of the same type flash off then on again. I cannot find a better way to do this conversion. I feel as thought there must still be some thing I am missing.
You could try using a PositionPropertyArray as the polygon's hierarchy with SampledPositionProperty for any dynamic positions and ConstantPositionProperty for any static positions. I'm not sure if it would perform any better than your solution, but it might be worth testing. Here is an example of how it might work that you can paste into the Cesium Sandcastle:
var viewer = new Cesium.Viewer('cesiumContainer', {});
// required if you want no interpolation of position between times
var noInterpolation = {
type: 'No Interpolation',
getRequiredDataPoints: function (degree) {
return 2;
},
interpolateOrderZero: function (x, xTable, yTable, yStride, result) {
if (!Cesium.defined(result)) {
result = new Array(yStride);
}
for (var i = 0; i < yStride; i++) {
result[i] = yTable[i];
}
return result;
}
};
var start = viewer.clock.currentTime;
// set up the sampled position property
var sampledPositionProperty = new Cesium.SampledPositionProperty();
sampledPositionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD;
sampledPositionProperty.addSample(start, new Cesium.Cartesian3.fromDegrees(0, 0)); // initial position
sampledPositionProperty.setInterpolationOptions({
interpolationAlgorithm: noInterpolation
});
// set up the sampled position property array
var positions = [
sampledPositionProperty,
new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 10)),
new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 0))
];
// add the polygon to Cesium viewer
var polygonEntity = new Cesium.Entity({
polygon: {
hierarchy: new Cesium.PositionPropertyArray(positions)
}
});
viewer.zoomTo(viewer.entities.add(polygonEntity));
// add a sample every second
var counter = 1;
setInterval(function(positionArray) {
var time = new Cesium.JulianDate.addSeconds(start, counter, new Cesium.JulianDate());
var position = new Cesium.Cartesian3.fromDegrees(-counter, 0);
positionArray[0].addSample(time, position);
counter++;
}.bind(this, positions), 1000);
One nice thing about this is you can set the timeline start/end time to a reasonable range and use it to see your polygon at any time within the sample range so you can see the history of your polygons through time (See here for how to change the timeline start/end time). Additionally, you don't need to use timers to set the positions, the time is built in to the SampledPositionProperty (although you can still add samples asynchronously).
However, this also means that the position depends on the current time in the timeline instead of a real-time array value. And you might need to keep track of a time somewhere if you aren't adding all the samples at once.
I've also never done this using ellipses before, but the semiMinorAxis and semiMajorAxis are properties, so you might still be able to use a SampledProperty.
Of course, this doesn't really matter if there are still performance issues. Hopefully it will improve as you don't need to recreate the array from scratch each callback and, depending on how you're getting the data to update the polygons, you might be able to add multiple samples at once. This is just speculation, but it's something to consider.
EDIT
Cesium can handle quite a bit of samples added to a sampled position, for example in the above code if you add a million samples to the position it takes a few seconds to load them all, but renders the polygon at any time without any performance issues. To test this, instead of adding samples using a timer, just add them all directly to the property.
for (var i = 0; i < 1000000; i++) {
var time = new Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
var position = new Cesium.Cartesian3.fromDegrees(-(i % 2), 0);
positions[0].addSample(time, position);
}
However, if you run into memory problems currently there is no way to remove samples from a position property without accessing private variables. A work around would be to periodically create a new array containing new position properties and use the previous position property array's setValue() method to clear previous values or perhaps to use a TimeIntervalCollectionProperty as in this answer and remove time intervals with the removeInterval method.

Actionscript 3- Random movement on stage? Also, Boundaries?

I'm trying to code something where there are creatures running back and forth, up and down across the stage, and I the player, have to try to go up to them, and pick them up. There are also boundaries on stage-
The map constraints- a big rectangle box is easy enough to accomplish. I've done this.
The boundaries within the map, which are also rectangles, but instead of bouncing the player back INSIDE the rectangle, I'm trying to do the opposite- keep the player out of it.
My code for it looks like this as of now:
//Conditions that check if player/monsters are hittesting the boxes (rocks
//and stuff), then if correct, bounce them away. Following code excludes
//the monsters for simplicity.
if((mcPlayer.x - aBounceBox[b].x) < 0 && mcPlayer.y <= (aBounceBox[b].y + aBounceBox[b].height/2) && mcPlayer.y >= (aBounceBox[b].y - aBounceBox[b].height/2))
{
mcPlayer.x = aBounceBox[b].x - aBounceBox[b].width/2 - mcPlayer.width/2;
}
//Duplicate above code for right side of box here
if((mcPlayer.y - (aBounceBox[b].y + aBounceBox[b].height/2)) < 0 && (mcPlayer.x + mcPlayer.width/2) > (aBounceBox[b].x - aBounceBox[b].width/2) && (mcPlayer.x - mcPlayer.width/2) < (aBounceBox[b].x + aBounceBox[b].width/2))
{
mcPlayer.y = aBounceBox[b].y + aBounceBox[b].height/2;
}
//Duplicate above code for Upper boundary of box here
The above doesn't work very well because the code to bounce for the left and right sides of the box conflicts with the upper and lower parts of the box I'm hit-testing for. Any ideas how to do that smoothly?
Also, another problem I am having is the pathing for the monsters in the game. I'm trying to get them to do the following:
Move around "organically", or a little randomly- move a little, stop. If they encounter a boundary, they'd stop and move, elsewhere. Not concerned where to, as long as they stop moving into rocks and trees, things like that.
Not overlap as much as possible as the move around on stage.
To push each other apart if they are overlapping, although I'd like to allow them to overlap very slightly.
I'm building that code slowly, but I thought I'd just ask if anyone has any ideas on how to do that.
To answer your first question, you may try to implement a new class/object which indicates the xy-offset between two display objects. In order to illustrate the idea more clearly, you can have a function similar to this:
public function getOffset(source:DisplayObject, target:DisplayObject):Object {
var dx:Number = target.x - source.x;
var dy:Number = target.y - source.y;
return { x:dx, y:dy };
}
Check if the hero character is colliding with another object first by hitTestObject(displayObj) of DisplayObject class. Proceed if the result is true.
Suppose you pass in your hero character as the source object, and another obstacle as the target object,
var offset:Object = getOffset(my_hero.mc, some_obstacle.mc);
After getting the resulting offset values, compare the magnitude (absolute value) of offset.x and offset.y. The outcome can be summarized as follows:
Let absDx be Math.abs(offset.x), absDy be Math.abs(offset.y),
absDx < absDy
offset.y < 0, target is above source
offset.y > 0, target is below source
absDx > absDy
offset.x < 0, target is to the left of source
offset.x > 0, target is to the right of source
absDx == absDy
refer to one of the above cases, doesn't really matter
Then you can update the position of your hero character according to different situations.
For your second question concerning implementing a very simple AI algorithm for your creatures, you can make use of the strategy above for your creatures to verify if they collide with any other stuff or not. If they do collide, assign them other directions of movement, or even simpler, just flip the signs(+/-) of their velocities and they will travel in opposite directions.
It is easier to implement simple algorithms first. Once it is working, you can apply whatever enhancements you like afterwards. For example, change directions when reaching junctions or per 3 seconds etc.

Square bracket notation to dynamically load library items?

So I've spent an embarrassing number of hours trying to save myself a few minutes and make my code a bit neater, and Google has produced nothing, so now I come crawling to stackoverflow. The problem is with square bracket notation + library items:
So let's say you have a MovieClip called "myMC_5", and you want to set its X position to 0..
myMC_5.x = 0;
..and if you don't want to hard-code the name of the MC but instead you want one line of code to move a specific MovieClip based on a variable, you could do something like this:
var selectMC = 5;
root["myMC_"+selectMC]x = 0;
..and this will have the exact same effect as myMC_5.x = 0, except that this time you must specify the location ("root" or "this" or something).
THE PROBLEM:
I'm working on a game in which the graphic for the background is loaded from the library, and it's different for each level. The initial loading of the vector from the library looks like this:
private var land:vector_land0 = new vector_land0();
..and this works fine, but it only loads that one specific vector. There should be about 30 or more. I'd like to just have 1 line of code in the constructor to load any of them, based on a variable which keeps track of the current level, like this:
private var land:["vector_land"+theLevel] = new ["vector_land"+theLevel]();
..but that doesn't work. I get syntax errors ("expecting identifier before leftbracket") because you need to specify the location of the object, like in the first example:
root["myMC_"+"whatever"].x = 0;
..but this library item has no "location". So, how the heck do I dynamically load a vector from the library? It's not "on the root", or anywhere else. It has no location. I refuse to believe that the standard method for accomplishing this is to create 30 different classes or write a giant block of code with 30 "if" statements, but searching Google has found nothing. :(
It sounds like you're looking for getDefinitionByName(), which you could use to do something like this:
import flash.utils.getDefinitionByName;
private var LevelVectorClass:Class = getDefinitionByName("vector_land" + theLevel) as Class;
private var land:Object = new LevelVectorClass();
This is a horrible way to solve the situation, I don't recommend using square brackets anywhere but arrays. I recommend you putting the "lands" into a LandContainer MovieClip, each frame of that MovieClip would container 1 graphic. It is much cleaner, and you could create constants to store the "identity" of the frames.Example:
var land:LandContainer = new LandContainer();
land.gotoAndStop(FIRST_LEVEL); //FIRST_LEVEL is a constant integer
You can even reuse this LandContainer instance because you can set it's visibility, remove from the display list, set it's frame to the next level without creating another instance. On second thought, I would write a wrapper for this class. Aka link it to your own class which extends the MovieClip class and create custom functions, fields... etc..
This dynamic thing is horrible, hard to maintain, and not efficient at all. Don't know why did not they delete it from AS3... they should have.

Read Text File with AS3 and Tween Each Word from Small to Large out of the screen

I am looking for some advice on the best way to read in like 200k words and have them each tween from the center of the screen as small dots and tween up to the word filling the SWF then "fly through my head" not literally, but you probably get it...
What would be the best way in AS3 to go about this? I am fairly new to it.
Thanks!
Depending on the origin of your text, you may have to use regular expressions to get rid of punctuation and replace any amount of spaces by a set delimiter.
You could then use the split method of the String class to turn your text into an Array of words.
Each word can then be assigned to a Textfield. Since the Textfield is a DisplayObject, all the manipulations you are mentioning above become possible.
You may be able to streamline all this by creating a class that extends Textfield and defining various methods for the motions you want to implement.
You'll probably want to look at the Timer class and some tweening libraries
Pseudo Code
- Clean up String with regular expressions
-> expected result var cleanString:String = "word1;word2;...wordn";
- Turn String into Array
var words:Array = cleanString.split( ";" );
- Create a class that extends Textfield and define a manipulate() method
var tf:MyTextField = new MyTextField();
//this method could take parameters
//such as x, y, scale , time , delay , ease etc...
tf.manipulate();
- Create an Array( Vector ) of Textfields
loop thru words Array to return array of Textfields
var objects:Array = [ tf1, tf2 , etc...]
- Manipulate objects
loop thru objects Array to manipulate them
if you don't want to do it by hand (like PatrickS described) maybe this will help you ...
http://www.greensock.com/splittextfield/