As3 hittesting doesn't really work (nested) - actionscript-3

I got a tank.
I got a hero (controllable character).
The tank has a nested movieclip which has a very thin surface area.
Yet, the thing detects a collision when it's not even touching the hero.
public class tank_sight extends MovieClip
{
private var _root:MovieClip;
public function tank_sight()
{
addEventListener(Event.ADDED, beginClass);
}
private function beginClass(event:Event):void
{
_root = MovieClip(root);
addEventListener(Event.ENTER_FRAME, loop);
}
private function loop(event:Event):void
{
if(this.hitTestObject(_root.hero.hitbox))
{
this.gotoAndStop(2);
trace("HIT");
fire();
}
else
{
this.gotoAndStop(1);
}
}
private function fire():void
{
var shell:Shell = new Shell(x, y, rotation - 180);
_root.addChild(shell);
}
}
What's wrong? I don't get it.
EDIT: Sight is rotating, so that's probably why. I tried using this code on the player class:
point = _root.tanks.barrel.sight.localToGlobal(new Point());
if(this.hitTestPoint(point.x, point.y, false))
{
trace("HIT");
}
But it doesn't work.. It never traces "HIT", unless I stand in some weird location at certain times.

hitTestObject works with nested objects also (display objects that have different parents), you should check logic of your game, and do some tests.
Simple example:
var squareHolder:Sprite = new Sprite();
var squareInner:Shape = new Shape();
var hitHolder:Sprite = new Sprite();
var hitCircle:Shape = new Shape();
hitCircle.graphics.beginFill(0x990000);
hitCircle.graphics.drawCircle(0, 0, 20);
squareInner.graphics.beginFill(0x009900);
squareInner.graphics.drawRect(0, 0, 40, 40);
addChild(squareHolder);
squareHolder.addChild(squareInner);
squareHolder.x = 200;
squareHolder.y = 100;
squareInner.x = 50;
squareInner.y = 50;
stage.addChild(hitHolder);
hitHolder.addChild(hitCircle);
hitCircle.transform.matrix = new Matrix(1, 0.5, 0.5, 1, 30, 30);
stage.addEventListener(MouseEvent.MOUSE_MOVE, function (e:MouseEvent):void {
hitHolder.x = e.stageX;
hitHolder.y = e.stageY;
if (hitCircle.hitTestObject(squareInner)) {
trace("Ding!");
}
});
Whatever you do with hitCircle (visible=false, trasparent fill, transformations) it will still work.

Related

AS3 Error 1009 when Debugging

I'm trying to create a game over screen for my space ship game, when player's shields reach 0 it goes to game over screen and stop the gameplay. The game over screen is working, but I can't stop the gameplay. I tried to set the Ship to null when player's shields reach 0 but I got error 1009. And all the gameplay objects (Ship, Enemy...) will loaded to the stage when "public function fGameStart(evt: Event): void {" executes, is there a way that I can stop this function from running when game over? Any help is greatly appreciated!
public class Engine extends MovieClip {
private var preloader: ThePreloader;
public function Engine() {
stage.addEventListener("gameSTART", fGameStart);
stage.addEventListener("gameOVER", fGameOver);
}
private var numStars: int = 80;
public static var enemyList: Array = new Array();
private var ourShip: Ship;
public function fGameStart(evt: Event): void {
ourShip = new Ship(stage);
ourShip.x = stage.stageWidth / 2;
ourShip.y = stage.stageHeight / 2;
ourShip.addEventListener("hit", shipHit, false, 0, true);
stage.addChild(ourShip);
for (var i: int = 0; i < numStars; i++) {
stage.addChildAt(new Star(stage), stage.getChildIndex(ourShip));
}
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
function loop(e: Event): void {
if (Math.floor(Math.random() * 20) == 5) {
var enemy: Stinger = new Stinger(stage, ourShip);
enemy.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy, false, 0, true);
enemy.addEventListener("killed", enemyKilled, false, 0, true);
enemyList.push(enemy);
stage.addChild(enemy);
}
else if (Math.floor(Math.random() * 80) == 5) {
var enemy2: Stinger2 = new Stinger2(stage, ourShip);
enemy2.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy, false, 0, true);
enemy2.addEventListener("killed", enemyKilled, false, 0, true);
enemyList.push(enemy2);
stage.addChild(enemy2);
}
}
}
public function fGameOver(e: Event) {
gotoAndStop(4);
ourShip = null;
}
}
There is no point setting the ourShip variable to null. It will not remove the DisplayObject from the stage, or remove it from memory. In fact the very reason you get this error is you setting it to null.
What you need to do is stop the loop function from triggering.
public function fGameOver(e: Event) {
gotoAndStop(4);
//ourShip = null;
stage.removeChild(ourShip);
removeEventListener(Event.ENTER_FRAME, loop);
}
also setting a weak reference for you listener might be a bad idea here
//addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
//why not:
addEventListener(Event.ENTER_FRAME, loop);

Draw over a movieclip

I have a paint application. I want to put a layer(movieclip with a square) and I want to draw over it.
Until now I cant paint but when I'm trying to add movieclip child, I cant draw over that movieclip. Which is the way to make something like this?
This is my code:
import flash.display.Sprite;
var myThick=10;
var currentColor = 0X000000;
var myCanvas:Sprite;
init();
function init():void {
drawCanvas();
var clearSwatch:Sprite = new Sprite();
clearSwatch.graphics.beginFill(0xFFFFFF);
clearSwatch.graphics.lineStyle(2, 0x000000);
clearSwatch.graphics.drawCircle(30, 370, 20);
clearSwatch.graphics.endFill();
addChild(clearSwatch);
clearSwatch.buttonMode = true;
clearSwatch.addEventListener(MouseEvent.CLICK, clearCanvas);//clear
}
function drawCanvas():void {
myCanvas = new Sprite();
myCanvas.graphics.beginFill(0xF0F0F0);
myCanvas.graphics.drawRect(60, 20, stage.stageWidth-80, stage.stageHeight+40);
myCanvas.graphics.endFill();
myCanvas.graphics.lineStyle(myThick, currentColor);
addChild(myCanvas);
myCanvas.addEventListener(MouseEvent.MOUSE_DOWN, startDraw);
//stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw);
myCanvas.buttonMode = true;
}
function startDraw(event:MouseEvent):void {
myCanvas.graphics.moveTo(event.localX, event.localY);
myCanvas.addEventListener(MouseEvent.MOUSE_MOVE, paintLine);
}
function stopDraw(event:MouseEvent):void {
myCanvas.removeEventListener(MouseEvent.MOUSE_MOVE, paintLine);
}
function paintLine(event:MouseEvent):void {
myCanvas.graphics.lineTo(event.localX, event.localY);
event.updateAfterEvent();
}
function clearCanvas(event:MouseEvent):void {
myCanvas.graphics.clear();
drawCanvas();
}
How can I make it work?
thank you!
Add both listeners to stage, along with mouse move listener, but use graphics commands over myCanvas.
stage.addEventListener(MouseEvent.MOUSE_DOWN, startDraw);
function startDraw(event:MouseEvent):void {
myCanvas.graphics.moveTo(event.localX, event.localY);
myCanvas.addEventListener(MouseEvent.MOUSE_MOVE, paintLine);
}
Etc.

Change Alpha on Rollover; Not Changing Back on Mouse Out?

So I have a Tile class, with a sprite variable that holds the graphic for the tile. On mouse over, I ColorTransform the graphic. Seems to work fine. On mouse out, I try to change it back. Nada. And in fact, rolling over the same tile twice will increase the alpha until eventually it fades totally. Not sure how to fix it. Here's my code.
In the Tile class:
this.addEventListener(MouseEvent.MOUSE_OVER, thisMouseOver);
this.addEventListener(MouseEvent.MOUSE_OUT, thisMouseOut );
public function thisMouseOver( e:Event ):void {
tileGraphic.bitmapData = setAlpha(tileGraphic.bitmapData);
}
public function thisMouseOut( e:Event ):void {
tileGraphic.bitmapData = resetAlpha(tileGraphic.bitmapData);
}
private function setAlpha( bmd:BitmapData ):BitmapData {
var rec:Rectangle = new Rectangle( 0, 0, bmd.width, bmd.height );
var ct:ColorTransform = new ColorTransform();
ct.alphaMultiplier = .65;
bmd.colorTransform( rec, ct );
return bmd;
} //end function setAlpha
private function resetAlpha( bmd:BitmapData ):BitmapData {
var rec:Rectangle = new Rectangle( 0, 0, bmd.width, bmd.height );
var ct:ColorTransform = new ColorTransform();
ct.alphaMultiplier = 1;
bmd.colorTransform( rec, ct );
return bmd;
} //end function resetAlpha
Can anyone point me in the right direction? Thanks!
Replace the resetAlpha with
private function resetAlpha( bmd:BitmapData ):BitmapData {
var rec:Rectangle = new Rectangle( 0, 0, bmd.width, bmd.height );
var ct:ColorTransform = new ColorTransform();
ct.alphaOffset = 255
bmd.colorTransform( rec, ct );
return bmd;
} //end function resetAlpha
You should better change alpha of the container instead of playing with the BitmapData pixels. For example, in your case if your tile bitmap will initially have transparent pixels (fill it with 0x00ff0000 prior to drawing a thing and check if so), they will become opaque with codingbuddha's answer. So, change the listeners to the following:
public function thisMouseOver( e:Event ):void {
tileGraphic.alpha=0.65;
}
public function thisMouseOut( e:Event ):void {
tileGraphic.alpha=1;
}

AS3 mouse events not firing when mouseX = 0

I'm creating what should be a very simple, full screen drag and drop game in Flash Develop. It works fine except in one frustrating instance.
I add items to the stage, add MOUSE_DOWN listeners to them and start dragging when one hears that listener. I then add a MOUSE_UP listener to figure out when to stop the drag. Again, this works fine unless mouseX = 0. When the mouse is all the way to the left of the screen and I mouse up or mouse down no listener is fired. I also took it out of full screen mode and if the mouse is at or below 0 no mouse events will fire.
What in the world is going on?
private function itemSelectedHandler(e:MouseEvent):void
{
thisItem = GameItem(e.currentTarget);
thisItem.startDrag();
stage.addEventListener(MouseEvent.MOUSE_UP, itemUnselectedHandler, false, 0, true);
}
private function itemUnselectedHandler(e:MouseEvent):void
{
stopDrag();
stage.removeEventListener(MouseEvent.MOUSE_UP, itemUnselectedHandler);
thisItem.removeEventListener(MouseEvent.MOUSE_DOWN, itemSelectedHandler);
}
You are calling stopDrag on the class and not the dragged sprite. Try something like the following :
package
{
public class Main extends Sprite
{
private var _draggedSprite:Sprite = null;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
for (var i:int = 0; i < 10; i++)
{
createBox();
}
}
private function createBox():void
{
var sp:Sprite = new Sprite();
sp.graphics.beginFill(0xff0000, 1);
sp.graphics.drawRect(0, 0, 30, 30);
sp.graphics.endFill();
sp.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
sp.x = Math.random() * (stage.stageWidth - 30);
sp.y = Math.random() * (stage.stageHeight - 30);
addChild(sp);
}
private function onMouseDown(e:MouseEvent):void
{
var sp:Sprite = e.target as Sprite;
sp.startDrag();
_draggedSprite = sp;
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
private function onMouseUp(e:MouseEvent):void
{
_draggedSprite.stopDrag();
_draggedSprite = null;
stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
}
}
}
This worked for me when mouseX=0 in fullscreen mode.

as3 draw line with LineStyle and then edit with transparent (invisible) pixels

I have MC with external loaded image (jpg).
Next layer is masked Sprite where I am drawing lines in different colors, opacity ,etc with graphics.lineStyle . Everything works fine, so I can see image and drawings over. Now I want eraser to make, and trying to make some kind of (transparent) line which will "erase" exising line.
If I for example draw (with mouse) red line, and then switch color to yellow and intersect on some point with previous line, it is ok, I see yellow color. Now I don't have idea how to make "transparent" color? So when mouse move over existing drawing, just see again part of background image. If I set one color background everything is OK, but I have image, end eraser leaves some color, and I need to be transparent. Tried blend modes, cached as bmp, everything I found but still no solution.
Seemingly you use Graphics objects that overlay the image. In order to erase something, you should give your sprite a topmost shape that will have BlendMode.ERASE as blend mode, and draw on it with alpha component of color being 0xFF. The sprite should have its blendMode to be BlendMode.LAYER. Here's a test to play with:
public class TestErase extends Sprite
{
public function TestErase() {
var b:Sprite = new Sprite();
var c:Shape = new Shape();
b.blendMode = BlendMode.LAYER;
c.blendMode = flash.display.BlendMode.ERASE;
c.graphics.beginFill(0, 1);
c.graphics.drawRect(50, 50, 50, 50);
c.graphics.endFill();
b.graphics.beginFill(0xff0000, 1);
b.graphics.drawRect(0, 0, 150, 150);
b.graphics.endFill();
b.addChild(c);
addChild(b);
this.graphics.beginFill(0x00c000, 1);
graphics.drawRect(75, 75, 100, 100);
graphics.endFill();
}
}
This makes a 50x50 hole, defined by "c" sprite in "b" sprite, that does not affect "this" sprite.
Okay you asked for an example. Here.
public class Main extends Sprite
{
public var sh:Shape;
public var drawing:Boolean;
public var sp:Sprite;
public var bd:BitmapData;
public var erasing:Boolean;
[Embed(source = '../lib/093.jpg')]
public static const Background:Class;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
var ba:Bitmap = new Background();
ba.scaleX = 0.5;
ba.scaleY = 0.5;
addChild(ba);
bd = new BitmapData(640, 400, true, 0);
var bm:Bitmap = new Bitmap(bd);
sp = new Sprite();
sp.addChild(bm);
sp.blendMode = BlendMode.LAYER;
var tf:TextField = new TextField();
tf.y = 401;
tf.background = true;
tf.backgroundColor = 0x0;
tf.defaultTextFormat = new TextFormat('Arial', 12, 0xe00000, true, false, false);
tf.selectable = false;
tf.text = 'Erase';
addChild(tf);
addChild(sp);
tf.addEventListener(MouseEvent.CLICK, toggleErasing);
sp.addEventListener(MouseEvent.MOUSE_DOWN, startDrawing);
sp.addEventListener(MouseEvent.ROLL_OUT, endDrawing);
sp.addEventListener(MouseEvent.MOUSE_UP, endDrawing);
}
private function toggleErasing(e:MouseEvent):void {
erasing = !erasing;
if (erasing) (e.currentTarget as TextField).text = 'Erasing...'; else (e.currentTarget as TextField).text = 'Erase';
e.stopPropagation();
}
private function startDrawing(e:MouseEvent):void {
if (drawing) return;
sh = new Shape();
sh.graphics.lineStyle(3, Math.floor(Math.random() * 0x100000), 1,true);
if (erasing) sh.blendMode = BlendMode.ERASE;
sh.graphics.moveTo(e.localX, e.localY);
addEventListener(MouseEvent.MOUSE_MOVE, trackMouse);
sp.addChild(sh);
drawing = true;
}
private function endDrawing(e:MouseEvent):void {
if (!drawing) return;
removeEventListener(MouseEvent.MOUSE_MOVE, trackMouse);
if (erasing) bd.draw(sh, null, null, BlendMode.ERASE);
else bd.draw(sh);
sp.removeChild(sh);
sh = null;
drawing = false;
}
private function trackMouse(e:MouseEvent):void {
sh.graphics.lineTo(e.localX, e.localY);
}
}
Tested by me, and should work as is, just change the background :)