AS3 Constrain Object while Zooming/Panning with GestureEvent / MouseEvent - actionscript-3

What I want to do is to keep my MovieClip (which is bigger than the viewport) from having its borders visible. Just like many apps do, e.g. Clash of Clans.
This way if you zoom in and zoom out or pan you will never see the stage under it.
If I was only to pan and not zoom it would be easy because I just had to calculate if my MC rectangle is within my stage and if yes i would just:
mc.x = 0 + mc.width / 2;
//or
mc.x = stage.StageWidth - (mc.width/2);
//or .... The same for y
Now the big problem is when I also zoom! And every time I come up with some code it doesn't work well. There are many examples for zooming via GestureEvent on the web but none of them keeps the MC constrained so you don't see the stage under it.
Can someone please provide an example for me. Lets say the stage is 480x720 and the mc is 1080x720!
You gotta pan and zoom while always covering the stage and the scale of mc will remain within 1-1.5.
Here is my current code:
Multitouch.inputMode = MultitouchInputMode.GESTURE;
stage.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);
stage.addEventListener(MouseEvent.CLICK, gotoBox);
var isInBox: Boolean = false;
table.x = 203.1;
table.y = 360;
table.scaleX = 1;
table.scaleY = 1;
var mouseTimer: Timer;
function onPan(event: TransformGestureEvent): void
{
if (!isInBox)
{
if (event.phase == GesturePhase.BEGIN)
{
stage.removeEventListener(MouseEvent.CLICK, gotoBox);
trace("noClick");
}
table.x += event.offsetX;
table.y += event.offsetY;
if (table.x > (table.width / 2))
{
table.x = table.width / 2;
}
if (table.x < stage.stageWidth - (table.width / 2))
{
table.x = stage.stageWidth - (table.width / 2)
}
if (table.y > (table.height / 2))
{
table.y = table.height / 2;
}
if (table.y < stage.stageHeight - (table.height / 2))
{
table.y = stage.stageHeight - (table.height / 2)
}
if (event.phase == GesturePhase.END)
{
if (mouseTimer !== null)
{
if (mouseTimer.running)
{
mouseTimer.stop();
mouseTimer.removeEventListener(TimerEvent.TIMER, enableClick);
}
}
mouseTimer = new Timer(250);
mouseTimer.addEventListener(TimerEvent.TIMER, enableClick);
mouseTimer.start();
trace("start");
}
}
}
function enableClick(e: TimerEvent)
{
mouseTimer.stop();
trace("stop");
mouseTimer.removeEventListener(TimerEvent.TIMER, enableClick);
stage.addEventListener(MouseEvent.CLICK, gotoBox);
trace("nowClick");
}
function gotoBox(e: MouseEvent)
{
// here it goes to another frame
}
I cannot add the zooming function because its a total disaster; I used something similar to the function onZoom in this link FlashAndMath
Because I needed to zoom in on a point and out from it, and that is the main issue as I have to move my mc around to make that point in the center WHILE I GOTTA MOVE IT TO MAKE MY WHOLE MC IN A CERTAIN BOUNDARY TO COVER THE STAGE! These too movements act against each other. If this last part is not clear ask me to explain more please:)
After having the right answer from LDMS I updated this question to avoid chat-discussions:)

All you need to do, is whenever you move or scale the object, check to make sure it's not going out of bounds.
So if you made a function called forceBounds, just call it anytime you scale or change the x/y:
function forceBounds():void {
//figure out the bounds to constrain to
//the x, should be the farthest left it can go without the edge being seen on the right. To get this value just subtract the smaller width (stage) from the larger width (mc) - this should be a negative number
var bounds:Rectangle = (stage.stageWidth - mc.width, stage.stageHeight - mc.height, mc.width - stage.stageWidth, mc.height - stage.stageHeight);
if (mc.x > bounds.x + bounds.width) {
mc.x = bounds.x + bounds.width;
}
else if (mc.x < bounds.x) {
mc.x= bounds.x;
}
if (mc.y > bounds.y + bounds.height) {
mc.y = bounds.y + bounds.height;
}
else if (mc.y < bounds.y) {
mc.y = bounds.y;
}
}
Here is a full example I made in FlashPro: (with an object on the the stage with an instance name of mc that is bigger than the stage bounds.
Zoom code taken from FlashAndMath.com
import flash.ui.Multitouch;
import flash.ui.MultitouchInputMode;
import flash.events.TransformGestureEvent;
import fl.motion.MatrixTransformer;
import flash.geom.Rectangle;
import flash.geom.Point;
import flash.events.GesturePhase;
Multitouch.inputMode = MultitouchInputMode.GESTURE;
stage.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);
stage.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
forceBounds();
function onPan(e:TransformGestureEvent):void {
trace("PAN");
if(e.phase == GesturePhase.BEGIN){
stage.removeEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
}
//localToGlobal is a helper method that converts a point to global coordinates
var p:Point = DisplayObject(e.target).localToGlobal(new Point(e.offsetX, e.offsetY));
//conversely, global to local does the opposite
p = mc.globalToLocal(p);
mc.x = p.x;
mc.y = p.y;
forceBounds();
if(e.phase == GesturePhase.END){
stage.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
}
}
function onZoom(event:TransformGestureEvent):void {
trace("ZOOM");
var container:MovieClip = mc;
var locX:Number=event.localX;
var locY:Number=event.localY;
var stX:Number=event.stageX;
var stY:Number=event.stageY;
var prevScale:Number=container.scaleX;
var mat:Matrix;
var externalPoint=new Point(stX,stY);
var internalPoint=new Point(locX,locY);
container.scaleX *= (event.scaleX + event.scaleY) * .5;
if(event.scaleX > 1 && container.scaleX > 6){
container.scaleX=prevScale;
}
if(event.scaleY > 1 && container.scaleY > 6){
container.scaleX=prevScale;
}
if(event.scaleX < 1 && container.scaleX < 0.8){
container.scaleX=prevScale;
}
if(event.scaleY < 1 && container.scaleY < 0.8){
container.scaleX=prevScale;
}
if(container.scaleX < 1) container.scaleX = 1;
if(container.scaleX > 1.5) container.scaleX = 1.5;
container.scaleY = container.scaleX;
mat=container.transform.matrix.clone();
MatrixTransformer.matchInternalPointWithExternal(mat,internalPoint,externalPoint);
container.transform.matrix=mat;
forceBounds();
}
function forceBounds():void {
//figure out the bounds to constrain to
//the x, should be the farthest left it can go without the edge being seen on the right. To get this value just subtract the smaller width (stage) from the larger width (mc) - this should be a negative number
var bounds:Rectangle = new Rectangle(stage.stageWidth - mc.width, stage.stageHeight - mc.height, mc.width - stage.stageWidth, mc.height - stage.stageHeight);
if (mc.x > bounds.x + bounds.width) {
mc.x = bounds.x + bounds.width;
}
else if (mc.x < bounds.x) {
mc.x= bounds.x;
}
if (mc.y > bounds.y + bounds.height) {
mc.y = bounds.y + bounds.height;
}
else if (mc.y < bounds.y) {
mc.y = bounds.y;
}
}

Related

Actionscript 3.0 Mouse trail snake game logic

I am developing a game in actionscript 3.0 (adobe flash) similar to this https://www.tvokids.com/preschool/games/caterpillar-count. I have the code for dragging the head of the snake in the direction of the mouse. However, I do not know how do I add the body of the snake and make it follow the path of the head. Following is my code to drag movieclip in the direction of the mouse :
var _isActive = true;
var _moveSpeedMax:Number = 1000;
var _rotateSpeedMax:Number = 15;
var _decay:Number = .98;
var _destinationX:int = 150;
var _destinationY:int = 150;
var _dx:Number = 0;
var _dy:Number = 0;
var _vx:Number = 0;
var _vy:Number = 0;
var _trueRotation:Number = 0;
var _player;
var i;
createPlayer();
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
function createPlayer():void{
_player = new head();
_player.x = stage.stageWidth / 2;
_player.y = stage.stageHeight / 2;
stage.addChild(_player);
}
function onDown(e:MouseEvent):void{
_isActive = true;
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
}
function onMove(e:MouseEvent):void{
updatePosition(_player);
updateRotation(_player);
}
function onUp(e:MouseEvent):void{
_isActive = false;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
}
function updatePosition(mc):void
{
// check if mouse is down
if (_isActive)
{
// update destination
_destinationX = stage.mouseX;
_destinationY = stage.mouseY;
// update velocity
_vx += (_destinationX - mc.x) / _moveSpeedMax;
_vy += (_destinationY - mc.y) / _moveSpeedMax;
}
else
{
// when mouse is not down, update velocity half of normal speed
_vx += (_destinationX - mc.x) / _moveSpeedMax * .25;
_vy += (_destinationY - mc.y) / _moveSpeedMax * .25;
}
// apply decay (drag)
_vx *= _decay;
_vy *= _decay;
// if close to target, slow down turn speed
if (getDistance(_dx, _dy) < 50)
{
_trueRotation *= .5;
}
// update position
mc.x += _vx;
mc.y += _vy;
}
function updateRotation(mc):void
{
// calculate rotation
_dx = mc.x - _destinationX;
_dy = mc.y - _destinationY;
// which way to rotate
var rotateTo:Number = getDegrees(getRadians(_dx, _dy));
// keep rotation positive, between 0 and 360 degrees
if (rotateTo > mc.rotation + 180) rotateTo -= 360;
if (rotateTo < mc.rotation - 180) rotateTo += 360;
// ease rotation
_trueRotation = (rotateTo - mc.rotation) / _rotateSpeedMax;
// update rotation
mc.rotation += _trueRotation;
}
function getDistance(delta_x:Number, delta_y:Number):Number
{
return Math.sqrt((delta_x*delta_x)+(delta_y*delta_y));
}
function getRadians(delta_x:Number, delta_y:Number):Number
{
var r:Number = Math.atan2(delta_y, delta_x);
if (delta_y < 0)
{
r += (2 * Math.PI);
}
return r;
}
function getDegrees(radians:Number):Number
{
return Math.floor(radians/(Math.PI/180));
}
The script below will not miraculously work on its own, however it has all the logic you need, well-explained. It makes a chain of any length follow its head by certain rules. I used the same principle here many years ago: http://delimiter.ru/games/25-lines/alone.html
// This one will represent the Mouse position.
var Rat:Sprite = new Sprite;
// The ordered list of chain elements.
// It all starts with the Mouse.
var Snake:Array = [Rat];
// Call this one each time you want to
// extend the snake with the piece of tail.
function addTail(aPiece:DisplayObject):void
{
// Get the last snake element.
var lastPiece:DisplayObject = Snake[Snake.length - 1];
// Sync the tail coordinates.
aPiece.x = lastPiece.x;
aPiece.y = lastPiece.y;
// Add the new piece to the snake.
Snake.push(aPiece);
}
// Add the pre-defined head as the first element.
addTail(SnakeHead);
// Now start following the Mouse.
addEventListener(Event.ENTER_FRAME, onFrame);
// Fires every frame and adjusts the whole snake, if needed.
function onFrame(e:Event):void
{
// Sync the attractor point with the Mouse.
Rat.x = mouseX;
Rat.y = mouseY;
// Now lets make each piece follow the previous piece,
// one by one, starting from the head, down to the tail.
for (var i:int = 1; i < Snake.length; i++)
{
followOne(Snake[i - 1], Snake[i]);
}
}
function followOne(A:DisplayObject, B:DisplayObject):void
{
// Think of these values as of vector
// pointing from B position to A position.
var dx:Number = A.x - B.x;
var dy:Number = A.y - B.y;
// Figure out the distance between the given pieces.
var aDist:Number = Math.sqrt(dx * dx + dy * dy);
// Do nothing if pieces are closer than 20 px apart.
// You can change this value to make snake shorter or longer.
if (aDist < 20)
{
return;
}
// This literally means "eat one tenth of the distance
// between me and the previous piece". If you want pieces
// to follow each other with more vigor, reduce this value,
// if you want the whole snake to slither smoothly, increase it.
B.x += dx / 10;
B.y += dy / 10;
// Rotate the B piece so it would look right into A's direction.
// Well, unless your pieces are round and look all the same.
B.rotation = Math.atan2(dy, dx) * 180 / Math.PI;
}

Creating boundaries for scrollRect for platformer game as3

Writing a side-scrolling platformer and figured that scrollRect would be best for memory, since it only renders the rectangle and not the entire stage. I have it centering my hero and scrolling with him. My problem is with creating the outer boundaries for the rectangle. if(view.x < 0) is easy. I have the x and y origins locked, it's the other edges where I have problems.It runs on ENTER_FRAME, btw. Here's my code:
public function ScrollWithHero()
{
var stageW2:Number = stage.stageWidth/2;
var stageH2:Number = stage.stageHeight/2;
var view:Rectangle = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);
if(hero.x - stageW2 > 0){ view.x = hero.x - stageW2; }
if(hero.x + stageW2 > levelWidth){ // stop scrolling the rect }
if(hero.y - stageH2 > 0){ view.y = hero.y - stageH2; }
if(hero.y + stageH2 < levelHeight){ // stop scrolling the rect }
scrollRect = view;
}
public function ScrollWithHero()
{
var stageW2:Number = stage.stageWidth/2;
var stageH2:Number = stage.stageHeight/2;
var view:Rectangle = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);
if(hero.x - stageW2 > 0)
{
view.x = hero.x - stageW2;
if(hero.x + stageW2 > levelWidth)
{
view.x = levelWidth - stage.stageWidth;
}
}
if(hero.y - stageH2 > 0)
{
view.y = hero.y - stageH2;
if(hero.y + stageH2 > levelHeight)
{
view.y = levelHeight - stage.stageHeight;
}
}
scrollRect = view;
}

How Do I make my ship stay on the stage?

What code should I put in here to make my ship stay in the stage?
My ship keeps going off the stage when I press the keys and it goes too far. How do I get it to bump into walls and make it stay on the stage?
package{
import flash.display.MovieClip;
import flash.events.Event;
import flash.ui.Keyboard;
public class Ship extends MovieClip{
var velocity:Number;
var shootLimiter:Number;
var health:Number;
var maxHealth:Number;
function Ship(){
velocity = 10;
shootLimiter = 0;
health = 100;
maxHealth = 100;
addEventListener("enterFrame", move);
}
function kill(){
var explosion = new Explosion();
stage.addChild(explosion);
explosion.x = this.x;
explosion.y = this.y;
removeEventListener("enterFrame", move);
this.visible = false;
Game.gameOver();
}
function takeDamage(d){
health -= d;
if(health<=0){
health = 0;
kill();
}
Game.healthMeter.bar.scaleX = health/maxHealth;
}
function move(e:Event){
shootLimiter += 1;
if(Key.isDown(Keyboard.D)){
this.x = this.x + velocity;
}
if(Key.isDown(Keyboard.A)){
this.x = this.x - velocity;
}
if(Key.isDown(Keyboard.W)){
this.y = this.y - velocity;
}
if(Key.isDown(Keyboard.S)){
this.y = this.y + velocity;
}
if(Key.isDown(Keyboard.SPACE) && shootLimiter > 8){
shootLimiter = 0;
var b = new Bullet();
stage.addChild(b);
b.x = this.x + 40;
b.y = this.y + -25;
}
if(shield.visible == true){
shield.alpha -= 0.0005;
if(shield.alpha == 0){
shield.visible = false;
shield.alpha = 1;
}
}
}
}
}
Your code is hard to fix as it is, but I'll give you an answer in pseudo, so you could think on how to implement it to fit in your project. Your ship is going out of the frame because it has no notion of where to stop. Lets say the ship x and y coordinates are taken from the upper left corner of the sprite, as it is in actionscript, then, every time your ship moves to the left, you must consider the following:
if(Key.isDown(Keyboard.A)){
var futureX:Number = this.x - velocity;
if (futureX <= 0){ //your ship has reached the left border of the screen
this.x = 0;
} else {
this.x = futureX;
}
}
things get a little more complicated when you're checking for the right or bottom border of the screen, as you need to consider the size of your stage and the size of your sprite in the calculations:
if(Key.isDown(Keyboard.S)){
var futureY:Number = this.y + velocity;
if (futureY + this.height >= Stage.stageHeight){
this.y = Stage.stageHeight - this.height;
} else {
this.y = futureY;
}
}
The solution for the top and right borders are trivial after this.

How do I resize an object in actionscript 3 without changing the hitbox?

When I resize a button in actionscript 3 the hitbox changes to the text instead of the square in my assignment example:
http://www.datafilehost.com/download-5ff20e2c.html
Video Explaining Issue: http://sdrv.ms/YcnjYV
Here is the code:
import flash.events.MouseEvent;
trace("Stage(X,Y):" + stage.stageWidth + "X" + stage.stageHeight);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mousePosition);
clickMe.addEventListener(MouseEvent.CLICK, handleClicks);
function mousePosition(event:MouseEvent) {
if(clickMe.mouseX >= 0 && clickMe.mouseX <= clickMe.width && clickMe.mouseY >= 0 && clickMe.mouseY <= clickMe.height)
{
do
{
var newX = Math.floor(Math.random()*stage.stageWidth);
var newY = Math.floor(Math.random()*stage.stageHeight)
}while(newX >= stage.stageWidth - clickMe.width || newY >= stage.stageHeight - clickMe.height)
clickMe.x = newX;
clickMe.y = newY;
if(clickMe.width > 50)
{
clickMe.width=clickMe.width - 5;
clickMe.height = clickMe.width - 5;
}
}
}
function handleClicks(event:MouseEvent)
{
trace("Button Clicked!");
}
How can I get the hitbox to stay the same when resizing an object?
this seems like a better and cleaner approach to what you are doing (assuming you move the clickMe clip to a random position and shrink it by 5px)
import flash.events.MouseEvent;
clickMe.addEventListener(MouseEvent.ROLL_OVER, moveSquare);
function moveSquare(e:MouseEvent):void
{
var newX = Math.floor(Math.random()*stage.stageWidth);
var newY = Math.floor(Math.random()*stage.stageHeight);
clickMe.x = newX;
clickMe.y = newY;
clickMe.width = clickMe.width - 5;
clickMe.height = clickMe.height - 5;
}
Changing of button size affects on mouseX/mouseY value inside it. So dont rely on it and just use checking based on button position and size.
if(mouseX >= clickMe.x && mouseX <= clickMe.x+clickMe.width && mouseY >= clickMe.y && mouseY <= clickMe.y+clickMe.height)

Actionscript 3 - Rotate symbol?

I'm working on a tutorial on youtube to learn some action-script 3. I've got my finished product which is basically a symbol that is known as ball and it has an instance called _ball. The finished product from the tutorial is shown here.
Tutorial Video - Youtube
So basically what I want to achieve is the ball to rotate, depending on which way the ball is moving how would I go about achieving this? I'm new to action-script so some code samples would be appreciated or a in depth explanation.
Incase anyone wants a copy of the code - this is it - I've edited it about in some ways but it doesnt effect much.
package
{
import flash.display.MovieClip
import flash.text.TextField
import flash.events.Event
import flash.events.MouseEvent
public class DocumentMain extends MovieClip
{
public const GRAVITY:Number = 2; // Declaring a const variable known as gravity
public const BOUNCE:Number = 0.8;
public const HIT:Number = 15;
public var _bounces:TextField;
public var _highscore:TextField;
public var _ball:Ball;
private var _vx:Number; // Declaring a variable known as _vx
private var _vy:Number; // Declaring a variable knwon as _vy
public function DocumentMain(): void
{
_vx = Math.random(); // Initalising _vx
_vy = Math.random(); // Initalising _vy
_ball.buttonMode = true;
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
}
private function enterFrameHandler (e:Event):void
{
// Gravitate the Ball
_vy += GRAVITY; // The ball is effected by gravity each frame
// Move The Ball
_ball.x += _vx;
_ball.y += _vy;
// Check Stage Boundaries For Collisions
checkBoundaryCollision();
}
private function mouseDownHandler (e:MouseEvent):void
{
// Hit the ball if it has been clicked
if (e.target == _ball)
{
hit(e.target.mouseX, e.target.mouseY);
}
}
private function checkBoundaryCollision():void
{
var left:Number;
var right:Number;
var bottom:Number;
var top:Number;
left = _ball.x - (_ball.width / 2);
right = _ball.x + (_ball.width / 2);
bottom = _ball.y + (_ball.height / 2);
top = _ball.y - (_ball.height / 2);
if (left < 0 && _vx < 0)
{
_ball.x = (_ball.width / 2)
_vx *= -1;
}
else if (right > stage.stageWidth && _vx > 0)
{
_ball.x = stage.stageWidth - (_ball.width / 2)
_vx *= -1;
}
if (top <= 42.70 && _vy < 0)
{
_ball.y = (_ball.height / 2)
_vy *= -1;
}
else if (bottom > stage.stageHeight && _vy > 0)
{
_ball.y = stage.stageHeight - (_ball.height/2)
_vy *= -BOUNCE;
_vx *= BOUNCE;
if (Number(_bounces.text) > Number(_highscore.text))
{
_highscore.text = _bounces.text;
}
_bounces.text = "0";
}
}
private function hit(hitX:Number, hitY:Number):void
{
// increment bounces
_bounces.text = String(Number(_bounces.text) + 1);
// Adjust vertical velocity
if (_vy > 0)
{
_vy *= -BOUNCE / 2;
}
_vy -= HIT;
//adjust horizontal veloity
if (_vx * hitX > 0)
{
_vx *= -BOUNCE;
}
_vx -= (hitX / _ball.width * HIT);
}
}
}
I can't test this right now, but it should be as simple as updating your enterFrameHandler to rotate the ball on each frame by a base value multiplied by the ball's x velocity:
public const ROTATION:Number = 1;
private function enterFrameHandler (e:Event):void
{
// Gravitate the Ball
_vy += GRAVITY; // The ball is effected by gravity each frame
// Move The Ball
_ball.x += _vx;
_ball.y += _vy;
// Rotate the ball each frame at a speed and direction
// related to the ball's current x velocity. A negative
// x velocity will result in a negative rotation.
_ball.rotation += ROTATION * _vx;
// Check Stage Boundaries For Collisions
checkBoundaryCollision();
}