Flash AS3: Keeping the mouse within certain boundaries - actionscript-3

So this one is a tricky one (for me) vital to the development of my project due to the fact that we can't directly modify the position of mouseX and mouseY - they are read-only variables.
Basically, what I want to do is have a player able to move their mouse only within a certain triangular area when a specific instance is active. The latter bit I can manage just fine, however I am having trouble restricting mouse movement -- or apparent mouse movement.
Here's what I have done so far:
1. Assign a library moveclip to the mouseX and mouseY position in the Event.ENTER_FRAME event - although I acknowledge that this should be moved to Mouse.MOUSE_MOVE. (this does not matter yet)
2. Using Corey O'Neils Collision detection kit, do a hit test on the border instances of the area with the crosshair/cursor.
3. Offset the cursor appropriately, and then set a standard Boolean value to false so that the cursor will not keep bouncing back into the cursor over and over.
My problem is, I am not sure what the best way is to go about allowing mouse movement again. Can anyone give me some tips on the best way to do this, or if necessary, point me in another direction where restricting mouse movement is a little easier?
For what it's worth, this is to stop users from aiming in an unrealistic direction with a character in a top-down (ish) shooter.
For those unfamiliar with Corey O'Neil's Collision Detection Kit, I believe it is just a pre-built setup of bitmap (or maybe vector) collision testing - I could be wrong. I'm not sure on the details of how it works, just its basic implementation.
Here is my code regarding mouse movement thus far:
import flash.ui.Mouse;
import flash.events.event
import com.coreyoneil.collision.CollisionList;
Mouse.hide();
var c:crosshair = new crosshair();
addchild(c);
var myCollisionList:CollisionList;
myCollisionList = new CollisionList(c); //sets up detection for the object c
myCollisionList.addItem(mcB); // adds mcB to the list of objects to check c's hittest with
function aim(e:Event) {
var collisions:Array = myCollisionList.checkCollisions();
if (collisions.length>0)
{
hashit = true; // tells the program that the mouse has collided with a boundary
c.x += 1;
c.y += 1;
}
else
{
if (hashit == false)
{
c.x = mouseX;
c.y = mouseY;
}
}
}
Apologies for the code block, but I figure it is best to show all relevant code -- I'm not sure about the complexity of this issue due to the read-only nature of the mouse's X and Y position.
Also, I'm looking for a possible solution which will not be clunky - that is, as soon as the mouse is back in the area, mouse movement will be smooth as it is originally, and where the cursor will still be matching the mouse position (meaning, the cursor is ALWAYS relevant to the mouse and will not change position should the mouse leave the boundaries).
Could anyone please give me some pointers? Sorry for the long question. I gather there might be a bit to get my head around here, being relatively new to AS3 - but I still feel this is a problem I can get past, if one of you can show me the right direction and help me with both the logic and programming side of things slightly.
Here is a diagram of my stage to clarify the boundary areas etc.
Thanks very much for any help in advance, I really do appreciate it!
Cheers, Harry.

How about trying getObjectsUnderPoint which returns an array of objects under a certain point.
If your triangle object is within the array the cursor must be above it.
var pt:Point = new Point(c.x, c.y);
var objects:Array = stage.getObjectsUnderPoint(pt);
if (objects.indexOf(triangleObject) > -1) {
trace("still within bounds");
}

The workaround here could be to hide the system mouse cursor and add a bespoke cursor movieclip to the stage.
Using a MOUSE_MOVE event listener attached to the stage, set the bespoke cursor movieclip to match the stage.mouseX and stage.mouseY values and also test whether the movieclip is outside your bounds. If so, set it back within your bounds.

Related

Flash AS3 Scripted eyes do not tween with other layer animation

thanks for reading.
I have a small animation in Flash that is scripted to enable the eyes to follow the mouse. This further animates upwards on mouse click, but the eyes that are scripted do not follow the tween.
I have childed/ embedded the eyes objects inside the main animating layer but this also seems to NOT follow.
I'm a bit confused and expect I have missed some fundamental structural/ layering issue -but I'm at a bit of a loss and am concerned if it is not me, then is it a bug or something in Flash and scripted layers working together or something:(
Anyway, I enclose the actual .fla and the .swf in vain of any help that you wonderful dudes can pass down to me.
https://drive.google.com/open?id=0B4yGmvZlwZmWanJJX1IzTk5pYXM
I would really love to know why and what if there is something I have fundamentally missed here. (I haven't checked for AS3 in the Symbol conversion Advanced options dialogue for the eye instance, but this has not effected the interactive eye part and I suspect it shouldn't be the cause of the conflict as a result - happy to be wrong here though of course :))
Edit: Here is the code for the project {which started out as a youtube tut showing how to control a circular movement of some eye objects with the mouseMove event }
//this is an action script window
//we can code into here :)
this.stop();
this.loop = false;
stage.addEventListener(MouseEvent.MOUSE_MOVE, MoveEyes);
stage.addEventListener(MouseEvent.MOUSE_DOWN, PlayTimeline);
function MoveEyes(e:MouseEvent): void
{
var mouseYPosition = mouseY - EyeR.y;
var mouseXPosition = mouseX - EyeR.x;
var radiusR = Math.atan2(mouseYPosition, mouseXPosition);
var degreesR = radiusR / (Math.PI / 180);
EyeR.rotation = degreesR;
mouseYPosition = mouseY - EyeL.y;
mouseXPosition = mouseX - EyeL.x;
var radiusL = Math.atan2(mouseYPosition, mouseXPosition);
var degreesL = radiusL / (Math.PI / 180);
EyeL.rotation = degreesL;
}
//when clicked start the animation
function PlayTimeline(e: MouseEvent) : void
{
this.play();
}
...In fairness, and I am totally happy to be wrong of course but, I don't think the code is causing or has anything to do with the fault, it may be more my stage layer positions or something along those lines, hence the full .fla file for someone better than me to point out my mistake.
Cheers all and thanks again for reading and taking the time here.
:)
Gruffy
That's actually a very interesting bug. I believe what's happening is that by interacting with the Eyes' properties, you're removing it from the timeline tween.
Your best bet would be to simply remake the timeline tween in code like so:
function PlayTimeline(e: MouseEvent) : void
{
this.play();
new Tween(EyeL, "y", fl.transitions.easing.None.easeInOut, EyeL.y, 141.95, 100);
new Tween(EyeR, "y", fl.transitions.easing.None.easeInOut, EyeR.y, 141.95, 100);
}
This gives the affect you need. However if you start messing with the timeline animation you will need to change the tween, so perhaps it would be best to move all the tweening code side?
EDIT: I notice that the eyes don't animate whilst the bird is moving unless you continue to move the mouse. The solution for this would be to change the mouseEvent listener to an enterframe listener so that it will happen every frame regardless of whether the mouse is moving or not. That's not the best solution, as it's a bit overkill, but to do anything else would likely involve some timers or third part libraries, which I don't think are strictly necessary at this point

AS3 collision detection in an object itself

I´m programing a space ship side scroll in as3. The bottom of the stage are mountains and here comes the problem, when I try to detect the ship collision against the mountains..
Because the poor collision detection and the need of avoid large loops my idea is create an object that works as a collider itself detecting a collision and avoiding parse all the stage or more selective metod.
I place "by hand" in the flash stage several instances of circles with a class for manage them where I place the If(this.collider.hits(ship)....
I spent looong time but I can find the way to make it work some of the mistakes i get are like this
Error 1061: Call to a possibly undefined method hitTestObject through a reference with static type Class.
some Idea? Thanks in advance
when you hit test with points it is important that the point being tested is relative to the object being tested against, eg
if(mountain.hitTestPoint(this.x + circle1.x, this.y + circle1.y))
will return true if the circles are inside the object calling the function because their position relative to the mountain is now relative to it rather then relative to the ships xy position within the clip... hope that makes sense.
btw I have done this myself in the past but I would have to remind you that you can only hit test with the points so there is no need to have circles, use blank sprites instead and set the visible flag in the properties panel to false, no drawing will make it slightly faster... not that you will notice, also sprites/graphics use less memory then movie clips.
also I would recommend hard coding some points in the clips rather then actually adding the clips in the sprite/clip itself, this will make it easier to work with them and scale later on (believe me this will annoy the hair from your head to do something later and slow the game to scale on the fly)
try something like this... you can determine the points values by adding a clip to the movie clip and getting its position from the properties if you must.
private var hitPoints:Vector.<Point> = new Vector.<Point>
hitPoints.push(new Point(10, 40));
hitPoints.push(new Point(30, 40));
//...do this for all your points
//loop through all your points and check if the hit relative to the ships position.
for(var i:int = 0; i < hitPoints.length; i++)
{
if (scene.hitTestPoint(ship.x + hitPoints[i].x, ship.y + hitPoints[i].y))
{
//do your hit stuff here
break;//don't forget to break
}
}
in this code you will need to make sure the scene object is a reference to your scenery at the bottom of the screen.
I hope this helps but if this is not enough help then you should post some of your code here so we can have a look and see where it can be improved.

How can I get coordinates of object collision in AS3?

I'm trying to write a simple flash game like Space Invaders. When the ship shoots I check if bullet hits the enemy with simple if like below:
if (bullet.hitTestObject(enemy)) {
var explosion = new Explosion(enemy.x, enemy.y);
stage.addChild(explosion);
explosions.push(explosion);
//Rest of logic like removing bullet and enemy from stage
}
What I expected to see was an Explosion instance appearing somewhere around the coords where bullet hit the enemy and that it would stay in place. Instead explosion seems to be appearing completely elsewhere and is moving in the same direction the enemy was (opposite direction to bullet). It seems that my assumption about successfully getting coordinates in a way presented above isn't right. Is there any other way to get it at least approximately? It doesn't have to be pixel-perfect, but I don't want explosion to appear on the other side of the stage.
Thanks for any suggestions.
It's not about getting the coordinates but having in mind where your children are added.
If the container is moved around, and you add the explosion to other container, the coordinates will mess up - each object has an internal coordinate system starting from 0,0.
Best advise would be to manually understand where's the difference and where are your children added. Common way to fix this is to add all your children at 0,0. This will give you full control and nevertheless an idea of how Flash works.
If you are unable to do so, you can always use globalToLocal and localToGlobal methods that will help you convert those coordinates to a global (Stage) ones, and vise versa.

Making a blocking obstacle

I'm new at ActionScript 3.0 so if you guys can help me a little.
I want to make an obstacle which block a path to player. I made this like that that I'm saving all movments to array and than if they collide it moves player to previous position. Is there another way because I think this is not the proper way to do it. And sometimes when it collides player is unable to move. Can you give me an example :)
Thanks
This is the only way you can ever detect a collision, but in a bit more refined way.
You actually collide the bodies (but do not apply the change to the actual object, yet).
Check for all colliding bodies on stage.
Take necessary step (roll back, destroy.. anything)
Apply the change & Render the bodies, on screen.
Considering the above as an example for flash :
var hero:Sprite = new Sprite();
addChild(hero);
while(1) {
var newX = hero.x + 1;
if(newX < 100)
hero.x = newX;
}
Every game should have a loop. The loop must branch out to various situations. So that's your start.
The hero object probably moves with the user interaction & the checks keep increasing, compelling you to re think the solution as your project grows more & more dense...

How to handle mouseEvent transparently in AS3?

I have a DisplayObject docked at the top of my interface that displays debug information (frames per second, etc.) and is translucent with an alpha of 60%.
I would like to interact with items under this surface, such that when the mouse rolls over it, it dims to 10% alpha, and mouse events pass through it to the underlying objects.
Normally, I have this debug info panel's mouseEnabled and mouseChildren properties set to false, so objects under it receive mouse events.
The problem is that in order to hide it when the mouse rolls over it, it needs to have mouseEnabled set to true. However, if mouseEnabled is true, the mouse events are not picked up by objects underneath it.
As far as I know, I can't selectively enable mouseEvents, so it's either going to receive them all or none of them. That means that I'd have to handle and forward ALL events, if I took that approach.
I really wish the mouseEnabled property had a "peek" mode or something, so that it could receive the events if it is on top, but also allow them to pass through to objects underneath.
If a DisplayObject has mouseEnabled=true it means that its events will be sent to its container not to whateve is underneath the object. So this solution will not work. The best solution would be to reroute events from it manually using getObjectsUnderPoint as described here.
I've been using this approach for years in multi-touch apps. With multiple touch points I don't see any processor overhead. And you got only one cursor.
I feel your pain. Unfortunately, I don't know of a way to enable/disable specific mouse events. You could get creative with the solution though. For instance, maybe try adding a MOUSE_MOVE listener to your stage and track the coordinates of the mouse. Then, if the stageX,stageY of the mouse is in the area of your panel, set the visibility. You might also be able to use getObjectsUnderPoint() to determine which objects are under the mouse. But, my guess is that it would get a little intense on the processor to run that on each frame iteration.
I believe you are looking for mouseEnabled = false
But another last ditch attempt you can do is on mouse over move it to the other side of the screen.
One approach you can take, although not ideal, is to add an enter frame listener and check the mouse position every frame. something along the lines of:
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
private function onEnterFrame(e:Event):void {
if(mouseX > width || mouseY > height){
//hide stats
}
}
I'm assuming you have this display hierarchy:
Debug Window
Debug Control 1
Debug Control 2
...
Overlay
Why not make the overlay a mask on DebugWindow and have your mouseEvents attached to DebugWindow itself? See this page for some inspiration: http://blog.shaperstudio.com/2010/11/as3-inverse-mask/
I had this same problem.. i made function to check is mouse over certain object:
public function isMouseOverObject(mPos: Point, pObject:DisplayObject, pContainer:DisplayObjectContainer) {
var under_objects:Array = pContainer.getObjectsUnderPoint(mPos);
var is_under:Boolean = false;
for (var i:int = 0; i < under_objects.length; i++) {
if (under_objects[i] == pObject) {
is_under = true;
break;
}
}
return is_under;
}