I have a sprite called food which adds to the screen via the use of a timer. more food items are added to the screen over time. How would i hit test all these food sprites with another object?
(the makeItem function creates the sprite btw)
private function placeFood(event:TimerEvent = null):void{
var rndI:uint = Math.floor(Math.random() * DIM); //sets a random integer based on the the floor
var rndJ:uint = Math.floor(Math.random() * DIM);
var rndX:Number = grid[rndI][rndJ].x; // sets a grid position for the food item to go
var rndY:Number = grid[rndI][rndJ].y;
food = makeItem(Math.random() * 0xFFFFFF);// random color
food.x = rndX;
food.y = rndY;
var foodArray:Array = new Array();
foodArray.push(food);
trace(foodArray.length)
addChild(food); //adds the food to the board
for (var i:uint = 0; i < snake.length; i++){
if (rndY == snake[i].y && rndX == snake[i].x){
placeFood();
}
}
}
Add the food items to an array and loop through that array doing hitTestObject. Something like:
var foods:Array = new Array();
foods.push(makeItem(blah));
...
for each (food in foods) {
food.hitTestObject(object);
}
It looks like you're putting items on a fixed grid. Do the food items move around? Your food does not move, and your snake (or whatever collides with the food) does, you can greatly optimize your collision detection by figuring out what grid square(s) the moving object occupies and only checking against food in that local area.
Generally speaking, when you need to do collision detection between many objects, you do it in multiple passes. The first pass would consist of the least computationally expensive checks to cull things that could not possibly be colliding such as objects that are very far apart, moving away from each other, etc. Your next pass might be something like simple boundary box or circle tests. Lastly, when you're down to the few items that pass all the cheap tests, you can use more expensive and accurate hit tests like pixel-by-pixel ones between hit masks.
Another way avoiding arrays is to use a sprite that contains all food. Every sprite is a collection of sprites and therefore a tree. That's what I use for hit detection: 1 special sprite contains only enemies / food. Run through all children and you don't even need to check the types of them. Cast them if needed.
// begin
var foodCollection: Sprite = new Sprite();
// time passes, food is added
foodCollection.addChild(food);
// hit test
for (var i:int = 0; i < foodCollection.NumChildren; i++)
{
var food: Sprite = foodCollection.getChildAt(i);
// test
}
Related
We own two objects in the scene. One follows the mouse position on the screen, and the object 2 in turn follows the route object 1 did. We are storing the positions covered by the object 1 and causing the object 2 play them.
When you run the game, an object follows the other quietly, reproducing the stored position ... but when one object's speed is changed (on mouse click increase velocity) the object 2 can not keep up, as this still following the positions already be cached in the array (including the calculations speed). Please, watch the shot video below:
YouTube: https://youtu.be/_HbP09A3cFA
public class Play : MonoBehaviour
{
public Transform obj;
private List<Recorder> recordList;
private float velocity = 10.0f;
private Transform clone;
void Start()
{
recordList = new List<Recorder>();
clone = obj;
}
void Update()
{
if (Input.GetMouseButton(0))
{
velocity = 20.0f;
}
else {
velocity = 10.0f;
}
var dir = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.AngleAxis(angle, Vector3.forward), 180 * Time.deltaTime);
transform.position += transform.right * Time.deltaTime * velocity;
Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y, Camera.main.transform.position.z);
recordList.Insert(0, new Recorder
{
Position = transform.position,
Rotation = transform.rotation,
Velocity = velocity
});
var x = 8;
if (x < recordList.Count)
{
clone.position = recordList[x].Position;
clone.rotation = recordList[x].Rotation;
clone.position += clone.right * Time.deltaTime * velocity;
}
if (recordList.Count > x)
recordList.RemoveRange(x, recordList.Count - x);
}
}
public class Recorder
{
public Vector3 Position{get;set;}
public Quaternion Rotation{get;set;}
public float Velocity{get;set;}
}
How can we play the positions stored always with the speed of the object 1?
Summary:
If the object 1 is slowly moving object 2 as well;
If the object 2 is running, the object 2 should do the route at a faster speed to always follow the object 1;
Thanks in advance.
If i understood correctly you might want to consider using Queue<T> instead of List<T>. I think it would be a better suited datatype as it represents a FIFO collection (first in, first out), which is how you use List anyway. You can add elements with Enqueue(T) to the end of the queue and always get the first item with Dequeue() (it also removes it). As for Stack<T> (the opposite), there is also a Peek() function which lets you "preview" the next element.
Another thing, it depends on distance and speed, but i have the feeling that storing the position of every frame could become a bit excessive (maybe im just overly concerned though)
I think the issue with your code is that you always get the 8th element of the List.
The app I'm working on has multiple nodes which are added repeatedly to the game. What I have been doing is creating new SKSpriteNodes using an array of texture, and once the node has been added, I assign that node with a specific physics body. But what I would like to do is instead of a having an array of textures, have an array of SKSpriteNodes with the physics body pre-assigned.
I believe I have figured out how to do this, the problem I have is when I try to add that SKSpriteNode more then once to the scene.
For example, below is I'm been playing around with, which works, but if I add another line of addChild(arraySegment_Type1[0] as SKSpriteNode), I get an error and the app crashes.
override func didMoveToView(view: SKView) {
anchorPoint = CGPointMake(0.5, 0.5)
var arraySegment_Type1:[SKSpriteNode] = []
var sprite = SKSpriteNode(imageNamed: "myimage")
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(sprite.size.width, sprite.size.height))
sprite.physicsBody?.dynamic = false
sprite.name = "mysprite"
arraySegment_Type1.append(sprite)
addChild(arraySegment_Type1[0] as SKSpriteNode)
}
Does anybody know how I would be able to load SKSpriteNodes multiple times from an array?
What your code does at the moment is the following:
Creates an empty array of the type [SKSpriteNode]
Creates a single SKSpriteNode instance
Ads a physicsBody to the sprite
Ads this single spriteNode to an array
Picks out the first object in the array (index 0) and ads it to the parentNode.
Your app crashes because you are trying to add the same SKSpriteNode instance to the scene several times over.
Here is a reworked version of your code that actually ads several sprites to the scene, it technically works but does not make a lot of sense just yet:
override func didMoveToView(view: SKView) {
// your ARRAY is not an array-segment it is a collection of sprites
var sprites:[SKSpriteNode] = []
let numberOfSprites = 16 // or any other number you'd like
// Creates 16 instances of a sprite an add them to the sprites array
for __ in 0..<numberOfSprites {
var sprite = SKSpriteNode(imageNamed: "myimage")
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
sprite.physicsBody?.dynamic = false
sprite.name = "mysprite" // not sure what your plans are here...
sprites.append(sprite)
}
// picks out each sprite in the sprites array
for sprite in sprites {
addChild(sprite)
}
}
Now as I said this doesn't make all that much sense. As you've probably noticed the sprites all appear on top of each other for one thing. More importantly, doing this all in the didMoveToView means it makes very little sense going through the whole array-dance, you could simply do the following instead:
override func didMoveToView(view: SKView) {
let numberOfSprites = 16 // or any other number you'd like
for __ in 0..<numberOfSprites {
var sprite = SKSpriteNode(imageNamed: "myimage")
// skipping the physicsBody stuff for now as it is not part of the question
addChild(sprite)
}
}
An improvement obviously, but all sprites are still covering each other, and didMoveToView might not be the best place to handle all this anyway, so perhaps something like this to illustrate the point:
override func didMoveToView(view: SKView) {
let sprites = spritesCollection(count: 16)
for sprite in sprites {
addChild(sprite)
}
}
private func spritesCollection(#count: Int) -> [SKSpriteNode] {
var sprites = [SKSpriteNode]()
for __ in 0..<count {
var sprite = SKSpriteNode(imageNamed: "myimage")
// skipping the physicsBody stuff for now as it is not part of the question
// giving the sprites a random position
let x = CGFloat(arc4random() % UInt32(size.width))
let y = CGFloat(arc4random() % UInt32(size.height))
sprite.position = CGPointMake(x, y)
sprites.append(sprite)
}
return sprites
}
Now, long-wound as this is it is still just a starting point towards a working and more sensible code-structure...
Hey everyone so I am having some trouble displaying the player Vector lives in a Array added to the stage. When I first start my game the lives are added correctly and when an enemy hits the player they are removed correctly but when I added a store where you can purchase an extra life I am getting a bug to where the vector lives are added to the stage but they dont remove when an enemy hits the player and they just are there.
in the constructor I have nLives = 2;
addPlayerLivesToStage();
Here is the addPlayerLivesToStage(); function as well as the removePlayerLives();
private function addPlayerLivesToStage():void
{
var startPoint:Point = new Point((stage.stageWidth / 2) + 240, (stage.stageHeight / 2) - 180);
var xSpacing:Number = 30;
lives = new Vector.<Sprite>();
for (var i:int = 0; i < nLives; i++)
{
var playerLives:mcPlayerLives = new mcPlayerLives();
playerLives.x = startPoint.x + (xSpacing * i);
playerLives.y = startPoint.y;
addChild(playerLives);
lives.push(playerLives);
//aPlayerLivesArray.push(playerLives);
}
}
private function removePlayerLive():void
{
//Remove live from screen
removeChild(lives[lives.length - 1]);
lives[lives.length - 1] = null;
lives.splice(lives.length - 1, 1);
}
Now in my games ENTER.FRAME Loop I have the function purchaseExtraLifeFunc(); that controls the Purchase and is supposed to add 1 life to the stage.
Like So:
private function purchaseExtraLifeFunc():void
{
if (nCoins >= 5 && purchaseItem)
{
trace("purchase life");
//remove coins from cost of purchase
nCoins -= 5;
updatecoinsTextScore();
//add extra life
nLives += 1;
addPlayerLivesToStage();
}
else
{
purchaseItem = false;
}
}
as you can say I add 1 Live and call the addPlayerLivesToStage function.
THis is not working correctly and I'm all out of ideas if anyone could help me with this I would really aprreciate it
I can see a few logic problems that could be the cause.
It looks like your addPlayerLivesToStage method ignores the fact that it could have been called before, and there could already be lives Sprites added to the stage. You then overwrite the lives Vector with a new Vector. So you don't have access to the previously added Sprites anymore.
I bet if you made the lives Sprites slightly transparent, you would see them stack over each other as you purchase more lives. Or to test, you could store a number that you increment every time you add a lives Sprite in your loop. And set the Y of the new sprite to this number. That way you would see all the Sprites you are adding.
To fix this, you want to remove all the lives Sprites at the beginning of your addPlayerLivesToStage function, because you are recreating them later in that function anyway.
EDIT*
The problem is still that the Sprites are not being cleaned up between addPlayerLivesToStage calls.
It is easier if you first use a container Spirte to hold all you life sprites. So add a private property to your class:
private var livesContainer:Sprite;
Then in you constructor, just set it up:
livesContainer = new Sprite();
livesContainer.x = (stage.stageWidth / 2) + 240;
livesContainer.y = (stage.stageHeight / 2) - 180;
addChild(livesContainer);
The you can just add all the lives into this container.
I modified your addPlayerLivesToStage function to remove any left over life sprites at the end, and cleaned up the adding, so it does not add a life that is already added.
private function addPlayerLivesToStage():void
{
var xSpacing:Number = 30;
// Add new life sprites if we need to.
for (var i:int = lives.length; i < nLives; i++)
{
var playerLives:mcPlayerLives = new mcPlayerLives();
playerLives.x = xSpacing * i;
livesContainer.addChild(playerLives);
lives.push(playerLives);
}
// Remove any life Sprites that are left over from last time.
for (var j:int = lives.length - 1; j > nLives; j--)
{
var life:Sprite = lives[j]
livesContainer.removeChild(life);
lives.splice(j, 1);
}
}
AS3 Newbie here... very confused, please be kind ;)
I have a MovieClip (rozette), which contains 7 instances of a MovieClip (circle). circle contains an animation and I would like to play each instance of it within rozette consecutively.
Questions - do I need to use an array? Is using eventListener the best way to do this? If so, how can I create an eventlistener for each item in the array? And what kind of event am I listening for?
Many thanks. Kat
You can see some options given as answers here:
Flash AS3 - Wait for MovieClip to complete
Basically I would think you want to have an array and a counter for which one you're on, each time the "playNext" function gets called, you should increment the counter and pull the next movie clip from the array. What this means is the order of elements in the array will dictate the order they play in. Something like:
private var myArray:Array = [mc1,mc2,mc3]; //list all the movieclip instance names here
private var counter:Number = 0; //start off on the first element, arrays in AS3 start at 0
//Play the next clip
private function playNext():void {
//Check that we're not at the end of the list, if so just return/stop
if(counter>myArray.length-1)
return;
//grab the next clip
var curClip:MovieClip = myArray[counter] as MovieClip;
//increment the counter for next time
counter++;
//when the last frame gets hit call this function again
curClip.addFrameScript(curClip.totalFrames-1, playNext);
//play the clip
curClip.play();
}
From your constructor of your Main class you would want to call playNext() once to get things rolling.
import com .greensock.*;
import com. greensock.easing.*;
var s1:b1=new b1();
var s2:b2=new b2();
var s3:b3=new b3();
var r1:Array = new Array();
r1.push(s1);
r1.push(s2);
r1.push(s3);
function ff (){
var i = 0;
while (i < r1.length) {
r1[i].visible=false
r1[0].visible=true
addChild(r1[i]);
i++;
}
TweenMax.staggerTo(r1, 0.4, {visible:true},0.2);
}
bb.addEventListener(MouseEvent.CLICK,cliki4x);
function cliki4x(e:Event):void{
ff ();
}
In a game I'm making, I'm trying to have sprites put on top of a hit box. The best way I could think of accomplishing this is to make two arrays; one for the hit boxes, and one for the sprites, and then have the sprites stay on top of their respective hit-box via a for loop that will be in its own script.
Problem is, is that when I try to get the MovieClips in either of the arrays to do anything, it doesn't work. If I do a trace on the X or Y location of the sprites, I get "undefined" in my terminal. I'm going to explain this from the top down.
Here is an excerpt from my class that contains the for loop (Dasengine is my main class fyi)
for(var i:Number = 0; i < Dasengine.ovrcnt.length; i++){
trace(Dasengine.ovrcnt[i].x); //returns "undefined"
trace(Dasengine.ovrcnt[i]); //returns "object Onmea"
Dasengine.ovrcnt[i].x = Dasengine.enemycnt[i].x;//this isn't working
}
In another script when an enemy spawns, they go through this method.
if(ENEMY SPAWN CONDITION IS MET ){
Dasengine.baddie = new nme_spawn.Enemya(); //ENEMY HITBOX
Dasengine.Obaddie = new nme_overlay.Onmea(); //ENEMY's sprite
Dasengine.enemycnt[cntup] = [Dasengine.baddie]; //Enemy's Hit box movie clip is put in array meant for holding enemy movie clips
Dasengine.ovrcnt[cntup] = [Dasengine.Obaddie]; //Enemy sprites that go over the hit boxes are stored here
cntup++; //this is so movie clips are put in new parts of the array
}
in my main class, the movie clips are declared as follows also I have the addChild functionality in there.
public static var Obaddie:nme_overlay.Onmea;
//^variable for sprite
public static var baddie:nme_spawn.Enemya;
//^variable for hitbox
also Obaddie= Overlay baddie. Its the MovieClip that acts as what goes on top of the hitbox, this is what the player will see
badde = is simply the hitbox. This contains the "meat" of the enemy ai.
I talked about this with some friends and they thought I might need to define what 'X' is inside of the class of the object that is in the array. So I did the following
package nme_overlay {
import flash.display.*;
import flash.events.*;
import nme_spawn.*;
public class Onmea extends MovieClip{
// Constants:
// Public Properties:
// Private Properties:
public static var xloc:int = 0;
// Initialization:
public function Onmea() {
this.addEventListener(Event.ENTER_FRAME, overlaya);
}
private function overlaya(e:Event){
xloc = 55;
//trace(xloc);
}
}
}
and then for the looping class I did this
for(var i:Number = 0; i < Dasengine.enemycnt.length; i++){
trace(Dasengine.ovrcnt[i]);//returns "object Onmea"
trace(Dasengine.ovrcnt[i].xloc);//still returns "undefined"
}
Your xloc variable is static--it belongs to nme_overlay, the Class, not any particular instance. If you were to do this in your code AND you had strict mode on (which I suspect you do not, because there's a lot of stuff in your code that should be giving you at least warnings), you'd get an error that would tell you exactly that:
for(var i:Number = 0; i < Dasengine.enemycnt.length; i++){
trace(Dasengine.ovrcnt[i]);//returns "object Onmea"
trace(nme_overlay(Dasengine.ovrcnt[i]).xloc);//still returns "undefined"
}