I'm writing a game where you have to go through a maze. I want this game to have different levels. But for each level, the maze is going to be different. So I drew other walls. But I do not want to write my collision detection method 50 times if I have 50 different levels.
I thought of a way of fixing it, but it's not working. I created a new symbol with nothing in it and named it wall. I think that I can make my wall = wall1 (another symbol I converted, and exported for as), and just do stage.addChild(wall). But I can't find a way to do that. So I need help!
Make a generic class e.g. Wall and make your library symbols use that for their base class. You won't need to create them at runtime using ActionScript for this inheritance to work, you can still just place your MovieClips on the stage.
The next thing you need to do is store these Walls somewhere. Because you seem inexperienced with ActionScript, and want to avoid writing code for new levels, you can automate this process using a manager type class. We will call this class WallManager and it will look like this:
public class WallManager
{
private static var _walls:Vector.<Wall> = new <Wall>[];
internal static function register(wall:Wall):void
{
_walls.push(wall);
}
public static function reset():void
{
_walls = new <Wall>[];
}
public static function get walls():Vector.<Wall>{ return _walls; }
}
Then we'll create your Wall class. Within the constructor for this class, we will automatically have the Wall add itself into the WallManager listing:
public class Wall extends Sprite
{
public function Wall()
{
WallManager.register(this);
}
public function touchingMouse(mouseX:int, mouseY:int):Boolean
{
// For this example I am checking for collisions with the
// mouse pointer. Replace this function with your own collision
// logic for whatever it is that is supposed to collide with
// these walls.
if(parent === null) return false;
var bounds:Rectangle = getBounds(parent);
return bounds.contains(mouseX, mouseY);
}
}
This setup is not 'best practice', but it is suitable in your situation because your project seems small, you appear to be working on it alone, it's simple and it gets the job done.
At the end of each level, use WallManager.reset() to remove the walls from the previous level. For checking collisions across all walls, just use a loop like this:
for each(var i:Wall in WallManager.walls)
{
var collision:Boolean = i.touchingMouse(mouseX, mouseY);
if(collision)
{
// There was a collision.
//
//
}
}
You can make one MovieClip with 50 frames saying stop() on the first frame and do your code like this:
private var wallnum:int;
public function Main()
{
stop();
wallnum = 1;
var wallobj = new Wall();
addChild(wallobj);
wallobj.gotoAndStop(wallnum);
}
For collision detection, I recommend Pixel Perfect Collision Detection (https://code.google.com/p/master-air-controller/source/browse/trunk/master-air-controller/src/PixelPerfectCollisionDetection.as?spec=svn6&r=6)
Related
I want to change the opacity of a specific MovieClip (named: Red_mc) within multiple layers of Movie Clips (example layer hierarchy : Character_mc > arm_mc > weapon_mc > Attribute_mc > Red_mc).
But I also have frame by frame animation within Character_mc (each containing and using the same MovieClip). I want the button to change the properties of all the Red_mc within each frame).
I've learn Adobe Animate for a while now but I've just started learning ActionScript recently,thus I'm very new in this language. Basically I'm just trying to make a somewhat "simple" character profile "page". I've tried a few method, but they have lots of limitation. Below is what I used for a single framed Movie clip
function fl_ClickToHide(event: MouseEvent): void {
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.IntelligenceW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.AgilityW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.StrengthW.visible = true;
}
button_7.addEventListener(MouseEvent.CLICK, fl_ClickToHide_2);
function fl_ClickToHide_2(event: MouseEvent): void {
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.StrengthW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.IntelligenceW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.AgilityW.visible = true;
}
button_8.addEventListener(MouseEvent.CLICK, fl_ClickToHide_3);
function fl_ClickToHide_3(event: MouseEvent): void {
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.StrengthW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.AgilityW.visible = false;
Idle_mc.Idle_hw_mc.CrystalW_mc.Attribute_mc.IntelligenceW.visible = true;
}
This works btu there's lots of limitaion, eg. when there's multiple single framed within the movie clip then it wouldn't work.
My goal is to make a button that when clicked, it'll search for a specific MovieClip and then edit the the properties of the Movie Clips within it.(ie. Red, green and Blue).
TD;DR: So is there a way for the code to search for the a target specific Movie Clip across multiple layers of Movie Clips within a frames?
thanks hope what i said make sense.
Info №1. Objects that are not in the current frame do not exist at the moment (for your script, at least).
Info №2. Mixing scripts and frames is a brave thing to do. Because there's a lot of pain and suffering and misery lies ahead once you decide to go that way.
If I had a task of programming a lot of pieces spread across complicated hierarchy, I think I'd do the following.
First, I'd devise a shared data class that is available from any point of your application.
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class AppData
{
static public const D:Object = new Object;
static public const E:Event = new Event(Event.CHANGE);
static public const I:EventDispatcher = new EventDispatcher;
static public function has(key:String):Boolean
{
return D.hasOwnProperty(key);
}
static public function read(key:String):*
{
return D[key];
}
static public function write(key:String, value:*):void
{
if (value === null)
{
delete D[key];
}
else
{
D[key] = value;
}
I.dispatchEvent(E);
}
}
}
Now, if you want certain clip to behave in a certain way, without actually knowing, where this clip is on your app hierarchy might be. For example, you want to control its alpha-transparency. On the first frame of this clip you do:
import AppData;
import flash.events.Event;
// The last argument is important, because timeline objects are
// auto-removed if their parent's timeline instructs so, thus
// you won't be able to locate them and unsubscribe, which,
// in turn, means they will hang in the memory forever.
// Still, if you subscribe them with useWeakReference
// set to true, they will be removed normally
// and unsubscribed automatically.
AppData.I.addEventListener(Event.CHANGE, onChange, false, 0, true);
// Call once in order to forcibly sync the object with the data.
onChange(null);
function onChange(e:Event):void
{
if (AppData.has("red.alpha"))
{
alpha = AppData.read("red.alpha");
}
else
{
alpha = 1;
}
}
Then, once you execute the following instruction, each and every object, watching the red.alpha setting will change its alpha:
import AppData;
AppData.write("red.alpha", 0.3);
The setup above is very primitive, and, probably, can be improved in a number of ways, but that greatly depends on understanding of what you are building there, which I don't have.
Kind of new Actionscript and I'm just trying to make a simple tamagoci game. I've wrote all the code out but and receiving no compiler errors but for some reason I'm also not receiving any output messages for my mouse event listeners. Here is all my code, I really can't find the problem and any help would be greatly appreciated. Thanks.
package{
import flash.display.MovieClip;
import flash.events.MouseEvent;
public class Main extends MovieClip{
public var feedButton:MovieClip;
public var tamagoci:MovieClip;
public var disButton:MovieClip;
public var dietButton:MovieClip;
public function Main() {
this.init();
}
private function init():void {
this.feedButton.addEventListener(MouseEvent.MOUSE_DOWN, onfeedMouseDownHandler);
this.disButton.addEventListener(MouseEvent.MOUSE_DOWN, ondisMouseDownHandler);
this.dietButton.addEventListener(MouseEvent.MOUSE_DOWN, ondietMouseDownHandler);
}
private function onfeedMouseDownHandler(event:MouseEvent)void{
this.tamagoci.scaleX += 0.1;
this.tamagoci.scaleY += 0.1;
}
private function ondisMouseDownHandler(event:MouseEvent)void{
this.tamagoci.gotoAndPlay(5);
}
private function ondietMouseDownHandler(event:MouseEvent)void{
this.tamagoci.scaleX -= 0.1;
this.tamagoci.scaleY -= 0.1;
}
Are you using Flash Professional?
You're declaring your variable types in your class here;
public var feedButton:MovieClip;
public var tamagoci:MovieClip;
public var disButton:MovieClip;
public var dietButton:MovieClip;
But then in your constructor, all you are doing is running init();
public function Main() {
this.init();
}
So, this could one of a few things. The most likely is that you have declared your variables, but you haven't initialised them. You've created the variables to hold your objects, but according to your code, they're empty. More specifically, a variable or class property that doesn't assign an object to a variable of an object type will contain a default value of null.
You could prove this in your code by simply putting a condition inside your init(); method;
if(tamagoci == null){
trace("I haven't been assigned an object of type class yet!")
}
So it could be 1 of these 3 things:
1: If you have written your own classes for these class properties/variables, then you need to instantiate them with the new keyword. The general syntax is;
variable_name = new ClassName(parameter_1, parameter_2);
If you are using classes you have written yourself, you have to create an instance of the object, assign it to a variable, and then add it to the stage using addChild();. For example, lets say you've written your own Tamagoci class;
tamagoci = new Tamagoci();
tamagoci.x = 100; // set the x location
tamagoci.y = 200; // set the y location
addChild(tamagoci);
Notice the use of Tamagoci. This is just an example, but this is the class name, which shouldn't be confused with variable/property name. It could just have easily been;
tamagoci = new MovieClip();
But then, this is just an empty MovieClip. It needs a property to display on the screen. A Shape, A Bitmap, or another container class object like MovieClip or Sprite (container classes allow you to nest display objects inside them). But on a basic level, it must contain a visual component to appear on the stage.
2:
Have you made Main your document class? This is the class which will get automatically called when your Flash movie plays. To set this, click on your stage, and in the properties dialogue box on the right, under PUBLISH, type in the name of your class, which is "Main".
3:
If you have created MovieClips in your library in Flash Professional, then you need to go to your library, right click the MovieClips, and select properties. From there, you need to make sure Export for Actionscript is ticked.
Now, if you click on your MovieClips on the stage, then open the Properties tab in the top right of Flash Professional's default layout, then right at the top should be a text field, and if you hover over it, Instance name will pop up as a tool tip. This is where you name your stage objects. Once that is done, you have access to them in your timeline.
If this is how you've done this, then you don't need to declare the variables in your main class, as they are already declared on your stage by Flash Professional and instantiated automatically.
I've been trying to find the best way to animate bezier curves with AS3. By this far following has been the best solution:
import flash.display.*;
import flash.display.Sprite;
import flash.geom.*;
import com.greensock.TweenMax;
import com.greensock.easing.*;
public class Waves extends MovieClip
{
public var piste:Number = stage.stageHeight;
public var piste2:Number = 0;
var a:Sprite = new Sprite();
var coord:Vector.<Number> = new Vector.<Number>();
var com:Vector.<int> = new Vector.<int>();
public function Waves()
{
addChild(a);
coord.push(0, 30);
com.push(1);
coord.push(260, piste, stage.stageWidth, 30);
com.push(3);
tweenNumbers();
}
public function tweenNumbers():void {
TweenMax.to(this, 0.45, {piste:piste2, repeat:-1, yoyo:true, immediateRender:true, ease:Expo.easeOut, onUpdate:draw});
}
public function draw():void {
coord[3] = piste;
a.graphics.clear();
a.graphics.lineStyle(1,0x990000,1);
a.graphics.drawPath(com, coord);
}
}
Do I really have to use graphics.clear to animate curves? Is there more efficient way? If I tween faster than 1 second, rendering lags and you can see the previous line, is there way to get rid of it?
Hmm. Perhaps you should post your used version of TweenMax to properly debug the issue. There seem to be several of them, some use asynchronusly dispatched "update" events, some employ an enterframe listener, thus making sure each update routine is called each frame. So, graphics jittering can occur in an asynchronus scenario.
On the other questions:
Yes, you have to redraw the graphics object in question, this involves calling graphics.clear(). See, the Graphics object is a blackbox entity, you can't directly reach a control point of a curve to tween it somehow. So, in order to change a point on a curve, you have to redraw it.
A more efficient way would be emulating a tween on your Sprite directly, via an enterframe listener and a function similar to Strong.easeOut used in tweening to interpolate coordinates. You will then get rid of the all extra framework included in TweenMax library and will get full control of the event and code flow. This, however, is some work to both emulate yoyo behavior, time setting behavior, framerate behavior (you can switch to "time=frame" approach, eliminating one of the issues) and easing behavior. The tweenNumbers will look like this:
var isYoyo:Boolean=false;
var currentFrame:int;
var maxFrame:int;
function easingFunction(frame:int,maxframe:int,a:Number,b:Number):Number {
var x:Number=Number(frame)/maxframe;
return a+(b-a)*(x*x*(3-2*x)); // 3x^2-2x^3, a double-easing Perlin function
// recreate your needed function here!
}
var piste1:Number=0; // storing start coordinate
private function tweenNumbers():void {
maxFrame=Math.round(0.45*stage.frameRate); // from seconds to frames
currentFrame=0;
isYoyo=false;
a.addEventListener(Event.ENTER_FRAME,onUpdate);
}
private function onUpdate(e:Event):void {
if (!isYoyo) {
currentFrame++;
if (currentFrame==maxFrame) isYoyo=true;
} else {
currentFrame--;
if (currentFrame==0) isYoyo=false;
} // advance time
coords[3]=easingFunction(currentFrame,maxFrame,piste1,piste2);
// tween the coords[3] manually
a.graphics.clear();
a.graphics.lineStyle(1,0x990000,1);
a.graphics.drawPath(com, coord);
// draw updated path
}
No guarantee of desynching, though, but will normally work. Also a desynch (seeing previous line) can possibly happen if you have set stage framerate too high, so the video subsystem of a target device can't draw as many frames at once.
Ok. I have a question about AS3 game structure. How to structure simple shooter game.
I have a main Hero and ships that shoot bulets. I want to add two levels but I am not sure how to structure them properly or which way is properly.
I have main class that holds everything and should switch trough levels. So I separate each level as a separate class.
Sample code
package
{
// imports
public class Main extends Sprite
{
// properties
private var testLevel:Level1;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
testLevel = new Level1();
addChild(testLevel);
}
}
}
// Level1 code
package Levels
{
// imports
public class Level1 extends Sprite
{
// properties
private var ship:Ship;
public function Level1(stage:Object)
{
// do some stuff
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(e:Event):void
{
// do some stuff
}
}
}
So According to this code my question is: Should I add ship bullets using separate ENTER_FRAME function inside Ship or should I add them in the level loop?
I can do both ways but which is better for performance and for mintenance, because I plan to add some actions when the bulet reaches the end of the sceen or when hits the Hero.
This is a though compromise:
For performance is better to have one single ENTER_FRAME listener.
But for maintenance it should be better to have separate update functions inside the Ship class and inside each object (for example, Enemy class, Bullet class etc) in the game.
So usually the preferred method to deal with this and get the best of both options is to have one main ENTER_FRAME listener on your main class, which is usually referred to as the Main Game Loop. This should be the only ENTER_FRAME listener running in the game. From this Game Loop you should invoke an update function for each object currently in the game, which is the responsible for updating the object's position, status, etc inside the game.
From my opinion - make a level engine and describe a level with xml instead of createing a class for each level.
Make a shoot engine that holds a bulletes and update tham. Make a collision enginge to check the colisions.
A good example is code of Waste Invaders. link
Check src / com / shooty / engine code this will help you a lot.
Lets say we have a movieclip "Enemy" in the Flash library and a class "Enemy.as" is associated with it which listens to ENTER_FRAME event as follows,
public function Enemy():void
{
//constructor of this "Enemy.as" class
addEventListener(Event.ENTER_FRAME, move);
}
private function move(evt:Event):void
{
x += 5;
}
Now my question is if this "Enemy.as" is instantiated in other class say "Main.as" which again uses ENTER_FRAME event on the same instantiated Enemy object as follows,
public function Main():void
{
//constructor of this "Main.as" class
enemy1 = new Enemy();
enemy1.addEventListener(Event.ENTER_FRAME, checkCollision);
}
private function checkCollision(evt:Event):void
{
if(enemy1.x == mainObj.x)
{
//do something
}
}
Is this the good approach in terms of optimization? Or should not use this approach at all?
From a performance point of view, it's always best to only listen for ENTER_FRAME in a single location (e.g. in your main application class) and to then invoke custom update() methods on all the objects that need to be updated. That's a very common approach for games for instance.
One reason why this is greatly superior from a performance point of view is that no new instances of Event need to be created. If you have 100 listeners (which is not uncommon when using the approach you outline) that means 100 new instances of the Event class every frame, and instantiating classes is among the heaviest things that you can do in Flash.
You rarely actually need the Event object in ENTER_FRAME handlers, so using the update() approach instead makes a lot of sense. If you can, try centralizing other events as well in performance critical applications like games.
As Richard says, it is much more efficient to use a single update (Game Loop) method when creating games. Not only from the performance point of view, but also because you get a lot more control using a single update. Like this:
public function update(deltaTime : Number) : void
{
var gameObject : IGameObject;
for(var i : int = 0 ; i < _gameObjects.length ; i++)
{
gameObject = _gameObjects[i];
gameObject.update(deltaTime);
}
...
}