How can hitTestObject work for many objects? - actionscript-3

I have a game where player moves forward over (semi)random tiles. I want a hittest for when the player hits a certain tile.
However when the char hits one of the spawned in floor2's nothing happens.
I think this is because there are multiple 'floor2' movieclips on the stage ?
When i trace the bounds "getBounds" for floor2 the positions it shows keeps changing anyway to Hitest with all of them ?
function handleCollision( e:Event ):void {
if(char.hitTestObject(floor2)){
trace("hit detected");
}
This is how the player spawns in:
var char:Char = new Char();
char.x = 275;
char.y = 786;
cam.addChild(char);
This is how floor2 spawns in :
if (randomRounded > 10 && randomRounded <= 50 ){
floor2 = new Floor2();
floor2.x = -8.45;
floor2.y = 786 - tileCounter;
cam.addChildAt(floor2, stage.numChildren-1);
Extra : ( RandomRounded is a randomly generated number ), ( there is a 'Var floor2:Floor2;')
please help :(

A variable can only ever reference up to one value. So your floor2 variable can only reference one Floor2 object. If you assign a new value, the variable will reference that value.
What you should do is to use an Array, which can hold many objects.
var floors:Array = [];
if (randomRounded > 10 && randomRounded <= 50 ){
floor2 = new Floor2();
floor2.x = -8.45;
floor2.y = 786 - tileCounter;
cam.addChildAt(floor2, stage.numChildren-1);
floors.push(floor2); // add the reference to the array, now floor2 can safely be overwritten by a new value without losing the previous one
}
In your function handleCollision you would then iterate over the array to test each individual floor object. Here's a quick untested example of how that could look like:
function handleCollision( e:Event ):void
{
for (var i:uint = 0; i< floors.length; ++i)
{
if(char.hitTestObject(floors[i]))
{
trace("hit detected");
}
}
}

Related

Actionscript: How can I create a counter that registers a hitTestObject and shows the score on the main stage?

My game counts the number of hits to an object and brings the user either to a winning or losing page. How can my hitTestObject count the number of hits while showing the number on the main stage? If the user hits "friend" 5 times, I want it to play the "youWin" layer and if they hit a "biter" once, I want it to play the "youLose" layer. (Please help this is for my final project and I'm almost done) Thank you! :)
stop();
addEventListener(Event.ENTER_FRAME,fishHit);
function fishHit(e:Event){
if (theFish.hitTestObject(biter)){
removeEventListener(Event.ENTER_FRAME,fishHit);
gotoAndPlay("youLose");
}
}
var theFish:fish = new fish();
theFish.x = 200
theFish.y = 260
addChild(theFish);
for (var which=0; which<5; which++){
var biter:shark=new shark();
biter.x=1400;
biter.y=int(Math.random()*660.0+30.0);
addChild(biter);
}
for (var what=0; what<5; what++){
var friend:starfish=new starfish();
friend.x=1400;
friend.y=int(Math.random()*660.0+30.0);
addChild(friend);
}
var counter : int = 0;
addEventListener(Event.ENTER_FRAME,winner);
function winner (e:Event){
if(theFish.hitTestObject(friend)) {
counter += 1
scoreboard.score_text.text = counter;
if(counter == 5)
removeEventListener(Event.ENTER_FRAME,winner);
gotoAndPlay("youWin");
}
}
You need many updates in your code, but I'll try to copy and paste your code with small modifications. You should define your variables outside of for loop, also you must add multiple objects like 'friends' to an array.
stop();
// arrays
var friends:Array = new Array();
var biters:Array = new Array();
var counter : int = 0;
var theFish:fish = new fish();
theFish.x = 200
theFish.y = 260
addChild(theFish);
for (var which=0; which<5; which++){
var biter = new shark();
biter.x=1400;
biter.y=int(Math.random()*660.0+30.0);
addChild(biter);
// push it to the array
biters.push(biter)
}
for (var what=0; what<5; what++){
var friend = new starfish();
friend.x=1400;
friend.y=int(Math.random()*660.0+30.0);
addChild(friend);
// push it to the array
friends.push(friend)
}
addEventListener(Event.ENTER_FRAME, enterFrame);
function enterFrame(e:Event){
// theFish vs biters
for (var i:int = 0; i < biters.length; i++){
if (theFish.hitTestObject(biters[i])){
removeEventListener(Event.ENTER_FRAME, enterFrame);
gotoAndPlay("youLose");
}
}
// theFish and friends
for (i = 0; i < friends.length; i++){
if(theFish.hitTestObject(friends[i])) {
// remove this friend so it does not increase counter
friends.splice(i,1);
counter += 1
scoreboard.score_text.text = counter;
if(counter == 5){
removeEventListener(Event.ENTER_FRAME, enterFrame);
gotoAndPlay("youWin");
}
}
}
}

AS3 shuffling movieclips

I've added the basic targets and applying drag and drop for my puzzle pieces, now Im having trouble making the shuffling aspect. As in, after the player completes or opens up the fla, each time will start the puzzle pieces in random places of the stage. I understand using arrays for shuffling somehow but Im not sure exactly how to achieve this. I've stored the instance of my 19 puzzle pieces inside the array but now I have no idea what to do with this array. Other tutorials were abit out of my league and leaves my head scratching.
Just started doing coding for flash professional so yeah, any help with the shuffling movie clips ie the puzzles pieces would be greatly appreciated.
Heres's my code, Im not posting the whole thing since from P1 to P19 is basically copy pasting:
import flash.events.Event;
stage.addEventListener(Event.ENTER_FRAME, EntFrame)
function EntFrame(e: Event) : void
{
P1.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void
{
P1.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void
{
P1.stopDrag();
}
if (T1.hitTestObject(P1.Tar1))
{
P1.x = 313.15;
P1.y = 242.75;
}
P19.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag_19);
function fl_ClickToDrag_19(event:MouseEvent):void
{
P19.startDrag();
}
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop_19);
function fl_ReleaseToDrop_19(event:MouseEvent):void
{
P19.stopDrag();
}
if (T19.hitTestObject(P19.Tar19))
{
P19.x = 624.35;
P19.y = 455.60;
}
}
Here is what I hope is more holistic answer.
First, ditch those inline functions. Right now you make an ENTER_FRAME listener and inside that function you have inline function defined. This means every frame tick (which is tied to your frame rate, not the main timeline), those functions are going to get created again, and since you are adding them as handlers for listeners, they will stay in memory forever.
Here is a way you code this, showing ways to reduce redundancy and get rid of those memory leaks. This assumes the following:
You have 19 objects on the stage called T1 - T19, that represent the possible locations the pieces can go.
You have 19 pieces on the stage called P1 - P19, that, and the numbers correlate to the T locations as per the correct location of the piece.
//let's create a function to randomize the piece location
function seedPieces() {
//create an array consisting of the integers 1 - 19
var unusedSpaces:Vector.<int> = new Vector.<int>;
var i:int;
for (i = 1; i <= 19; i++) {
//populate that array
unusedSpaces.push(i);
}
var curLocation:DisplayObject; //helper var for the loop below
var curPiece:Sprite; //helper var for the loop below
//loop 19 times (from 1 - 19) - one iteration for each piece
for (i = 1; i <= 19; i++) {
curPiece = this["P" + i] as Sprite; //you can get the piece this way, or use an array if you've made one, like `pieces[i];`
trace(curPiece.name);
//splice removes and returns the item at the specified index (in this case a random number between 0 and arrays length less 1) - the second parameter is amount of items to remove (just 1 for this case)
curLocation = this["T" + unusedSpaces.splice(int(Math.random() * unusedSpaces.length), 1)] as DisplayObject;
trace(" ",curLocation.name);
//move the piece to the random location:
curPiece.x = curLocation.x;
curPiece.y = curLocation.y;
}
}
//NOW, as an aside, you should use a loop to add all your listeners for the sake of sanity - if you have them in an array, loop through that, or use the sloppy way like this:
for (var i:int = 1; i <= 19; i++) {
Sprite(this["P" + i]).addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
}
//create a var to hold any piece that is currently being dragged, so you know which piece to stop drag on later
var currentDraggingItem:Sprite;
seedPieces();
function fl_ClickToDrag(event:MouseEvent):void
{
//assign this clicked item to the currentDraggingItem var
currentDraggingItem = event.currentTarget as Sprite;
//bring this one to the front
currentDraggingItem.parent.addChild(currentDraggingItem);
//you can use this one click handler for all pieces
//the piece that was actually clicked, is referenced by event.currentTarget
currentDraggingItem.startDrag();
//add the mouse up listener now that the mouse is currently DOWN
stage.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
//listen every frame while dragging
stage.addEventListener(Event.ENTER_FRAME, EntFrame);
}
function fl_ReleaseToDrop(event:MouseEvent):void
{
//if currentDraggingItem has a value, stop drag it
if (currentDraggingItem) {
currentDraggingItem.stopDrag();
//send to the back
currentDraggingItem.parent.addChildAt(currentDraggingItem,0);
}
//remove the mouse up and enter frame listener now that the mouse is UP
stage.removeEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
stage.removeEventListener(Event.ENTER_FRAME, EntFrame);
if(checkComplete()){
//game over, do something
}
}
function EntFrame(e: Event) : void
{
//this will snap the peice to the correct spot when the mouse is touching the correct spot
if(currentDraggingItem){
if (this[currentDraggingItem.name.replace("P","T")].hitTestPoint(mouseX,mouseY))
{
currentDraggingItem.x = this[currentDraggingItem.name.replace("P","T")].x;
currentDraggingItem.y = this[currentDraggingItem.name.replace("P","T")].y;
}
}
}
function checkComplete():Boolean {
//use a loop to go through all your pieces and check if they are in the right spot. Again, you could have them in an array, or do it the lazy way
for (var i:int = 1; i <= 19; i++) {
if (!this["T"+i].hitTestObject(this["P"+i]))
{
return false;
}
}
return true;
}
Well, in general you can shuffle with the following code:
var shuffledVector:Vector.<someClass> = new Vector.<someClass>;
while (originalVector.length > 0) {
shuffledVector.push(originalVector.splice(Math.random() * originalVector.length, 1)[0]);
}
Longer, explained version:
var shuffledVector:Vector.<someClass> = new Vector.<someClass>; //We will store our shuffled vector in here
var randomIndex:int; //Random index from the originalVector
var resultVector:Vector.<someClass>; //result from the originalVector.splice(...) function
var randomElement:someClass; //Random element from the originalVector
while (originalVector.length > 0) { //We will reduce the size of the originalVector until the originalVector is empty.
randomIndex = Math.random() * originalVector.length; //Calculate a random index within the range of the originalVector from 0 to originalVector.lenght-1 (note that the range decreases by one on every loop)
randomVector = originalVector.splice(randomIndex, 1); //Use splice to remove one element at the randomly choosen index, we will receive a vector with the removed element...
randomElement = randomVector[0]; //...so we need to access the element
shuffledVector.push(randomElement); //Add the randomly choosen element to our shuffled vector
}
I've written the code for a vector as i suggest to use a vector instead of an array, but the principle behind it is the same for an array.
In your case the originalVector is a vector filled with your P1-P19 Movieclips and someClass would be MovieClip. The originalVector is empty at the end and could be replaced with the shuffled one and of course it would make a lot more sense if you put the code in a seperate function like this:
function Shuffle(originalVector:Vector.<someClass>) : void {
var shuffledVector:Vector.<someClass> = new Vector.<someClass>;
while (originalVector.length > 0) {
shuffledVector.push(originalVector.splice(Math.random() * originalVector.length, 1)[0]);
}
originalVector = shuffledVector;
}
Offtopic, but important for further coding: Someone else already mentioned, that it is not good to add EventListeners on every frame, because it is absolutely unnecessary. You only need to add the Listeners once. Your code is very repetitive, you should use a function which accepts a MovieClip, x and y then call that function 19 times.
e.g.:
function setUpMovieClip(MC:MovieClip, x:int, y:int) : {
MC.addEventListener(MouseEvent.MOUSE_DOWN, clickToDrag);
//more code...
}
within the clickToDrag function you can access the MovieClip which was clicked via the event.target property:
function clickToDrag(e:MouseEvent) : {
e.target.startDrag();
//more code...
}
I hope you get the idea.

changing a target crosshair color in actionscript 3

I am stuck at a very basic problem which I am not quite able to figure out. I have 4 alienships and a crosshair which moves around with the mouse. I want to change the color of my crosshair to red when it is near one of the alienships. Both my alienships and crosshair are classes which extend movieclip. I use them in my main document class inside a main game loop which is triggered by the Enter_Frame event listener.
Here is my logic that checks for the same
private var objColor:ColorTransform = new ColorTransform();
private function overlayCursorMove() : void {
var initialColor:ColorTransform = new ColorTransform();
initialColor.color = 0xD3D3D3;
for(var i = 0; i < alienShipArray.length; i++){
var currentShip:AlienShip = alienShipArray[i];
if(currentShip.getDistance(overlayCursor.x - currentShip.x, overlayCursor.y - currentShip.y) <= 30 ){
overlayCursor.transform.colorTransform = objColor;
}
else if(currentShip.getDistance(overlayCursor.x - currentShip.x, overlayCursor.y - currentShip.y) > 30 ){
overlayCursor.transform.colorTransform = initialColor;
}
}
}
Below is the custom getDistance function inside the AlienShip class
public function getDistance(delta_x:Number, delta_y:Number):Number
{
return Math.sqrt((delta_x*delta_x)+(delta_y*delta_y));
}
overlayCursor is my crosshair which moves around with my mouseX and mouseY positions. I have 4 alienships on my stage and all of them are stored inside the alienShipArray.
I am just calculating the distance between my crosshair and the currentship position to check if its less than a certain amount to change the crosshair color else if distance is greater change it back to previous color.
Surprisingly the logic only works for the very last ship in the array that is when the loop reaches i = 3 ( alienShipArray[3] ). For the rest of the ship the color of the crosshair stays the same!
You need to break out of your loop when you encounter an intersection, otherwise your loop will continue to run and the other non-intersecting aliens will cause the else if part of your condition to test true, and your crosshair will be changed back to its initial color.
That would explain why your code only works with the last alien in the loop, as there are no further iterations in that instance to reset the color.
Something like the following should do it (untested):
private var objColor:ColorTransform = new ColorTransform();
private function overlayCursorMove() : void {
var initialColor:ColorTransform = new ColorTransform();
initialColor.color = 0xD3D3D3;
for(var i:uint = 0; i < alienShipArray.length; i++) {
var currentShip:AlienShip = alienShipArray[i];
// Reset cursor back to initial color
overlayCursor.transform.colorTransform = initialColor;
if(currentShip.getDistance(overlayCursor.x - currentShip.x, overlayCursor.y - currentShip.y) <= 30 ) {
// Intersection found, change the cursor color
overlayCursor.transform.colorTransform = objColor;
// And break out of the loop so further iterations don't reset it
break;
}
}
// No intersections found, cursor remains in its initial state
}

AS3 Object Bin and Reset Button

I am trying to create a game for kids where they can drag letters on to a stage to make words.
I want to add a 'trash can' where users can drag letters they no longer need to dispose of them. I have created the movie clip but am totally unsure how to make it function using AS3.
I would also like to add a reset button so that the stage reverts to it's original state. Again, I have drawn it up and added the little as3 that I am aware of (to make it a button) but if anyone could assist with how to actually make this happen, I would be grateful.
The files are here: SWF | FLA and the code for the game is as follows:
import flash.display.MovieClip;
for (var i=1; i<27; i++)
{
this["object" + i].addEventListener(MouseEvent.MOUSE_DOWN, onStart);
this["object" + i].addEventListener(MouseEvent.MOUSE_UP, onStop);
}
var sx = 0,sy = 0;
function onStart(e)
{
sx = e.currentTarget.x;
sy = e.currentTarget.y;
e.currentTarget.startDrag();
}
function onStop(e)
{
if ( e.target.dropTarget != null &&
e.target.dropTarget.parent == dest &&
e.currentTarget.name != "copy" )
{
var objectClass:Class =
getDefinitionByName(getQualifiedClassName(e.currentTarget)) as Class;
var copy:MovieClip = new objectClass();
copy.name = "copy";
this.addChild(copy);
copy.x = e.currentTarget.x;
copy.y = e.currentTarget.y;
e.currentTarget.x = sx;
e.currentTarget.y = sy;
copy.addEventListener(MouseEvent.MOUSE_DOWN, onStart);
copy.addEventListener(MouseEvent.MOUSE_UP, onStop);
}
e.currentTarget.stopDrag();
}
resetButton.addEventListener(MouseEvent.CLICK, reset);
resetButton.buttonMode = true;
function reset(event:MouseEvent):void
{
//Not sure what AS3 to add here to reset to original state
}
I have already gave you the solution here Flash AS3 Clone, Drag and Drop
Here, I am providing a detail solution on how to drag objects inside a bin and remove them.
For dropping copied objects inside a bin, after dragging is stopped, check collision with bin object. for more info see,
copiedObject.hitTestObject(binObject)
For e.g.
First create trash-can MovieClip on the stage and give it an instance name 'trashCan' and add following lines to your onStop()(below e.currentTarget.stopDrag();)function like so:
UPDATE:
var copiedObjsArr:Array = [];
function onStop(e)
{
if ( e.target.dropTarget != null &&
e.target.dropTarget.parent == dest &&
e.currentTarget.name != "copy" )
{
//Code here remains same
//.......
//Keep collecting copied letters for further access in `reset()` function
copiedObjsArr.push(copy);
}
else if(e.currentTarget.name == "copy") //this is 'else if' (newly added)
{
var tarObject:MovieClip = e.currentTarget;
// These detects collision of dragged object with the trashCan
if(tarObject.hitTestObject(trashCan)) {
//These removes dragged object from the display list (not from the memory)
removeChild(tarObject);
tarObject = null; //to garbage
}
}
e.currentTarget.stopDrag();
}
And your reset() becomes like so:
function reset(event:MouseEvent):void
{
if(copiedObjsArr.length > 0)
{
//Traverse through all copied letters
for(var i:int = 0; i<copiedObjsArr.length; i++)
{
var objToRemove:MovieClip = copiedObjsArr[i];
removeChild(objToRemove);
objToRemove = null;
}
//Finally empty the array
copiedObjsArr = [];
}
}

AS3 Error #1009: Cannot access a property or method of a null object reference

Basically, I'm trying to make a randomly generated character follow a series of waypoints to get to where he needs to go without walking into walls etc on stage.
I'm doing this by passing an Array of Points from the Engine to the Character's followPath function (this will be on a loop, but I haven't gotten to that stage yet).
A part of this followPath function is to detect when the character is close enough to the waypoint and then move on to the next one. To do this, I'm trying to use Point.distance(p1,p2) to calculate the distance between the currently selected waypoint, and a point that represents the Character's current position.
This is where I'm running into this problem. Trying to update the current (x,y) point position of the Character is proving difficult. For some reason, the Point.setTo function does not seem to exist, despite it being in documentation. As a result, I'm using
currentPos.x = x;
currentPos.y = y;
//update current position point x and y
to try and do this, which is where my 1009 error is coming from.
Here is my full Character class so far:
package {
import flash.display.MovieClip;
import flash.geom.Point;
public class Character extends MovieClip {
public var charType:String;
private var charList:Array = ["oldguy","cloudhead","tvhead","fatguy","speakerhead"];
private var numChars:int = charList.length;
private var wpIndex:int = 0;
private var waypoint:Point;
private var currentPos:Point;
private var wpDist:Number;
private var moveSpeed:Number = 5;
//frame labels we will need: charType+["_walkingfront", "_walkingside", "_walkingback", "_touchon", "_touchonreaction", "_sitting"/"_idle", "_reaction1", "_reaction2", "_reaction3", "_reaction4"]
public function Character() {
trace("new character:");
charType = charList[Math.floor(Math.random()*(numChars))];
//chooses a random character type based on a random number from 0-'numchars'
trace("char type: " + charType);
gotoAndStop(charType+"_walkingfront");
x = 600;
y = 240;
}
public function followPath(path:Array):void {
if(wpIndex > path.length){ //if the path has been finished
gotoAndStop(charType+"_sitting"); //sit down
return;//quit
}
waypoint = path[wpIndex];
//choose the selected waypoint
currentPos.x = x;
currentPos.y = y;
//update current position point x and y
wpDist = Point.distance(currentPos,waypoint);
//calculate distance
if(wpDist < 3){ //if the character is close enough to the waypoint
wpIndex++; //go to the next waypoint
return; //stop for now
}
moveTo(waypoint);
}
public function moveTo(wp:Point):void {
if(wp.x > currentPos.x){
currentPos.x += moveSpeed;
} else if(wp.x < currentPos.x){
currentPos.x -= moveSpeed;
}
if(wp.y > currentPos.y){
currentPos.y += moveSpeed;
} else if(wp.y < currentPos.y){
currentPos.y -= moveSpeed;
}
}
}
}
Can anybody explain to me why this is happening? It's a roadblock that I haven't been able to overcome at this stage.
I'm also curious if anybody can provide information as to why I can't use the phantom Point.setTo method.
You're trying to assign x and y properties of a Point object that doesn't exist.
You have to create your Point:
currentPos = new Point ();
and then assign x and y
The problem is that your are not using the Point constructor first.
When you create a variable that is not a simple data type (Int, Number, String ...) you must call the constructor first and assign properties to the fields of the object only afterwards.
This is because you must initialize an instance of the class Point before accessing it's properties.
The same will be true with any other class.
http://en.wikipedia.org/wiki/Constructor_%28object-oriented_programming%29
"In object-oriented programming, a constructor (sometimes shortened to ctor) in a class is a special type of subroutine called at the creation of an object. It prepares the new object for use.."
Basically, you did not prepare a new Point object.
In this example, during the constructor (public function Character)
public function Character() {
//add these lines (you can omit the zeroes as the default value is zero)
//I added the zeroes to show that the constructor can set the initial values.
wayPoint = new Point(0, 0);
currentPos = new Point(0, 0);
trace("new character:");
charType = charList[Math.floor(Math.random()*(numChars))];
//chooses a random character type based on a random number from 0-'numchars'
trace("char type: " + charType);
gotoAndStop(charType+"_walkingfront");
x = 600;
y = 240;
}
remember every new object identifer references NULL (nothing) until you construct an object or do something like this
var pointA = pointB;
//where pointB is already not null
//You can also check this
if(currentPos != null)
{
currentPos.x = X;
currentPos.y = Y;
}
currentPos will not be null if you use a constructor first.
Good luck.