I have a class UglyLobster in a sidescroller-type game that uses artificial intelligence in AS3. UglyLobster is a largely self-operating class that determines what it will do next based on the environment. As I am building this artificial intelligence, I find the easiest solution is to use nested functions.
However, I am uncertain if nested functions will affect the performance and cause drag. This UglyLobster is not limited to one instance; many will be running at the same time. I've added code to show what I'm trying to do.
public class UglyLobster extends Mob {
//these vars determine what the UglyLobster will do next
public var behavior = "aggro"
private var behaviorStep:int = 1
public function UglyLobster() {
addEventListener(Event.ENTER_FRAME,performBehavior)
}
public function performBehavior(e) {
if (behavior == "aggro") {
aggro()
}
if (behavior == "tranq") {
tranq()
}
}
private function aggro() {
//it is necessary to have multiple steps.
//in step 1, the Lobster could bare its lobster fangs,
//then in step 2, crawl toward a point
//and in step 3, pinch somebody
if (behaviorStep == 1) {
step1()
}
if (behaviorStep == 2) {
step2()
}
if (behaviorStep == 3) {
step3()
}
function step1() {
if (someCondition) {
behaviorStep = 2
} else {
//do whatever happens in step1
}
}
function step2() {
if (someOtherCondition) {
behaviorStep = 3
} else {
//do whatever happens in step2
}
}
function step3() {
if (anotherCondition) {
behaviorStep = 1
} else {
behavior = "tranq"
}
}
}
private function tranq() {
//do nothing
}
}
Are the nested functions a memory-efficient way to complete the task? If not, how would you set up an artificial intelligence for UglyLobster? Examples would really help
Even if i don't like this kind of design and probably, as Jason suggested, there is an architectural design problem, i think there is no need for nested functions.
private function aggro() {
//it is necessary to have multiple steps.
//in step 1, the Lobster could bare its lobster fangs,
//then in step 2, crawl toward a point
//and in step 3, pinch somebody
if (behaviorStep == 1) {
step1()
}
if (behaviorStep == 2) {
step2()
}
if (behaviorStep == 3) {
step3()
}
}
private function step1() {
if (someCondition) {
behaviorStep = 2
} else {
//do whatever happens in step1
}
}
private function step2() {
if (someOtherCondition) {
behaviorStep = 3
} else {
//do whatever happens in step2
}
}
private function step3() {
if (anotherCondition) {
behaviorStep = 1
} else {
behavior = "tranq"
}
}
Related
A problem I've been running into lately involves trying to change a variable indirectly.
e.g.
var health:int = 100;
var currentVar = health; //I want currentVar to equal "health"
var currentVar -= 50; // I want to subtract 50 from health;
trace(health) // 100
I could set health to equal currentVar afterward, but this is not possible in some situations.
How can I make as3 understand I don't want current var to equal what health equals, but health itself?
You cannot create a reference (or pointer) to a basic type in As3.
They are always passed by value.
But you can do this with objects for example.
In your case, an object oriented approach is probably better anyway.
Who's health is it? Who's mana is is?
It's probably one thing that has both mana and health (and probably many other properties)
So whatever function is trying to modify different properties, should receive the whole object instead.
You can (should) define a class that contains all those properties with according setter and getter functions.
Than accept that class as a parameter.
Within that function you can decide what property to modify.
Here's one example how that could look like:
package
{
import flash.display.AVM1Movie;
import flash.display.Sprite;
public class FlashTest extends Sprite
{
public function FlashTest()
{
var hero:Hero = new Hero();
trace(hero.health, hero.mana);
dealDamage(hero);
trace(hero.health, hero.mana);
absorbEnergy(hero);
trace(hero.health, hero.mana);
doRandomThing(hero);
}
private function dealDamage(hero:Hero):void
{
hero.health -= 5;
}
private function absorbEnergy(hero:Hero):void
{
hero.mana -= 5;
}
private function doRandomThing(hero:Hero):void
{
if (Math.random() >= 0.5)
{
hero.health -= 15;
}
else
{
hero.mana -= 15;
}
}
}
}
internal class Hero
{
private var _health:uint = 100;
private var _mana:uint = 100;
public function set health(value:uint):void
{
_health = Math.max(0, value);
if(_health == 0)
{
trace("I'm dead");
}
}
public function get health():uint
{
return _health;
}
public function set mana(value:uint):void
{
_mana = Math.max(0, value);
if(_mana == 0)
{
trace("No more magic");
}
}
public function get mana():uint
{
return _mana;
}
}
I'm trying to call the punch function from my player class inside another class but for some reason it gives me this error:
1180: Call to a possibly undefined method Punch.
I'm not sure why it gives me this error. I even made the functions public.
This is the class where i am calling it from:
package
{
public class Player extends MovieClip
{
public function Player()
{
stage.addEventListener(KeyboardEvent.KEY_DOWN,KeyDown);
stage.addEventListener(KeyboardEvent.KEY_DOWN,KeyPressed);
addEventListener(Event.ENTER_FRAME,Update);
}
function KeyPressed(event:KeyboardEvent):void
{
//If on floor
if (CanJump)
{
//If A key is down
if (event.keyCode == 65)
{
//Punch
Punch(true);
}
}
}
function Update(event:Event)
{
//Do stuff
}
}
}
and this is what I am trying to call:
package
{
public class ComboSequence extends ComboHandler
{
public function ComboSequence(clipName:String, par:BaseObject, _oList:ObjectList)
{
// constructor code
super(clipName, par, _oList);
}
public function Punch(PunchKey:Boolean)
{
if (currentAttack != null)
{
if (Recovery <= 0 && FollowUpTime > 0)
{
currentAttack = currentAttack.GetNextAttack(PunchKey);
if (currentAttack != null)
{
SetAnimation(currentAttack.animName);
Recovery = currentAttack.Recovery;
FollowUpTime = currentAttack.Recovery + 25;
}
}
}
if (FollowUpTime > 0)
{
FollowUpTime--;
}
else
{
currentAttack = null;
}
if (Recovery > 0)
{
Recovery--;
}
}
}
}
When you call Punch() on its own inside the player class, ActionScript is looking for a Player.Punch() method. Your method is on the ComboSequence class. You're probably trying to do something like this:
var comboSequence:ComboSequence = new ComboSequence();
comboSequence.Punch()
Keep in mind that while this code will run, it probably won't do what you want it to do. I guess you want to keep an instance of ComboSequence in your player object. If that doesn't make sense to you, it would be a good idea to do some background reading on ActionScript and object-oriented programming in general.
You need to import ComboSequence into the Player class and call Punch though that like
ComboSequence.Punch. andypaxo is right in his post, you'd need to instantiate it somewhere within the Player class.
One note about your code, though, you should not name functions with a capital letter. Class names typically start with a capital letter, but not the methods within.
package
{
import ComboSequence; //you may need the package path if its in a package, something like com.classes.ComboSequence, where com.classes is the folder that ComboSequence is saved.
public class Player extends MovieClip
{
public function Player()
{
stage.addEventListener(KeyboardEvent.KEY_DOWN,KeyDown);
stage.addEventListener(KeyboardEvent.KEY_DOWN,KeyPressed);
addEventListener(Event.ENTER_FRAME,Update);
}
function KeyPressed(event:KeyboardEvent):void
{
//If on floor
if (CanJump)
{
//If A key is down
if (event.keyCode == 65)
{
//Punch
ComboSequence.Punch(true);
}
}
}
function Update(event:Event)
{
//Do stuff
}
}
}
also, regarding your "missing rightParen" error, rightParen means right parenthesis, or ), leftParen would be (. That error means you are missing a ) somewhere. Often times it can mean you added an extra one somewhere, thus closing a section of parenthesis you didn't mean to close, which would leave one unpaired parenthesis somewhere.
In Flash I'm calling authenticate first after constructing, how to avoid popup blocker.
public function FacebookProxy(appID:String, permissions:Object) {
this.appID = appID;
this.permissions = permissions;
}
public function authenticate():void {
var response:FacebookAuthResponse = Facebook.getAuthResponse();
if(response && response.uid) {
this.success(response);
} else {
this.init();
}
}
protected function init():void {
Security.loadPolicyFile("https://fbcdn-profile-a.akamaihd.net/crossdomain.xml");
Facebook.init(this.appID, this.initHandler);
}
protected function initHandler(response:FacebookAuthResponse, fail:Object):void {
if(response && response.uid){
this.success(response);
} else {
setTimeout(this.login, 200);
}
}
protected function login():void {
Facebook.login(loginHandler, this.permissions);
}
protected function loginHandler(response:FacebookAuthResponse, fail:Object):void {
if(response && response.uid) {
this.success(response);
} else {
//ExternalInterface.call("trace", "code:" + fail.error.code + ", message:" + fail.error.message + ", type:" + fail.error.type);
}
}
protected function success(response:FacebookAuthResponse):void {
}
I dont know flash, but you cant call Facebook.init, Facebook.login automatically, it will be caught by popup blockers.
Fire these two methods on users activity only i.e. on a click of user.
From what I remember, the most frictionless way of logging into Facebook (going by game apps, no popular, and one permissions screen) is to log in using the js api and pass the details into flash
Whats a better way to remove an old image or an image that is not there at all?
I am working with this XML slideshow and I have tweaked it the way I want, except the first image that loads doesn't disappear. How can I make this statement better? I've tried if (previous || imgHolder.numChildren > 0 ) as well and didn't work. Any help?
package
{
import flash.display.MovieClip;
import com.greensock.*;
import com.greensock.loading.*;
import com.greensock.events.LoaderEvent;
import com.greensock.loading.display.*;
public class Main extends MovieClip
{
var xml:XMLLoader;
var images:Array;
var current:int = 0;
var previous:Number;
public function Main()
{
init();
}
private function init()
{
LoaderMax.activate([ImageLoader]);
xml = new XMLLoader("xml/gallery.xml",{name:"loader",onComplete:onXmlLoaded});
xml.load();
}
function onXmlLoaded(e:LoaderEvent)
{
images = LoaderMax.getContent("loader");
nextImage(); //nextImage can use image var
}
private function nextImage()
{
TweenLite.from(images[current],1,{alpha:0, onStart:currentImage, onComplete: updateImage});
}
private function currentImage():void
{
imgHolder.addChild(images[current]);
}
private function updateImage()
{
if (previous)
{
imgHolder.removeChild(images[previous]);
}
previous = current;
if (current < images.length - 1)
{
current++;
} else
{
current = 0;
}
TweenLite.delayedCall(2, nextImage);
}
}
}
You could manipulate the contents of the Array to keep track of your current and previous slide index. The first in the Array is your current slide and the last is the previous slide. Like you suggested a numChildren check on your imgHolder is a good way to check if there is something to remove.
private function nextImage() : void
{
TweenLite.from(images[0], 1 , {alpha:0, onStart:currentImage, onComplete:updateImage});
}
private function currentImage():void
{
imgHolder.addChild(images[0]);
}
private function updateImage() : void
{
if(imgHolder.numChildren > 1)
{
imgHolder.removeChild(images[images.length-1]);
}
images.push(images.shift());
TweenLite.delayedCall(2, nextImage);
}
Is there a way in spark datagrid to disable some rows programmatically, in flex 3, it could be done using the function mouseEventToItemRenderer this way:
override protected function mouseEventToItemRenderer (
event: MouseEvent): IListItemRenderer {
var listItem: IListItemRenderer;// = super.mouseEventToItemRenderer(event);
if (_disableFlag)
{
if (listItem)
{
if (listItem.data)
{
if (disabledRow(listItem.data))
{
return null;
}
}
}
}
return listItem;
}
And then I implement the function disabledRow to return true or false depending on some condition, the condition that will specify if the selected item will be renderered or not. Is there a way in spark datagrid to do the same?
I think the spark datagrid supports the gridrollover event. under that we can get the
itemrender i think this might be suitable for you
for further reference please refer the Documentation
Ok, so here's how I did
First Step
I created the classes SelectionIndicator, HoverIndicator and CaretIndicator like the ones we find as skin parts in the datagrid skin, so the skin part caretIndicator:
<!--- #private -->
<fx:Component id="caretIndicator">
<s:Rect implements="spark.components.gridClasses.IGridVisualElement">
<fx:Script>
<![CDATA[
import spark.components.DataGrid;
import spark.components.Grid;
/**
* #private
*/
public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
{
const dataGrid:DataGrid = grid.dataGrid;
if (!dataGrid)
return;
const color:uint = dataGrid.getStyle("caretColor");
caretIndicatorFill.color = color;
}
]]>
</fx:Script>
<s:stroke>
<!--- #private -->
<s:SolidColorStroke id="caretIndicatorFill" color="0x0167FF" weight="1"/>
</s:stroke>
</s:Rect>
</fx:Component>
becomes
package com.tn.zuro.components
{
import mx.graphics.SolidColorStroke;
import spark.components.DataGrid;
import spark.components.Grid;
import spark.components.gridClasses.IGridVisualElement;
import spark.primitives.Rect;
public class CaretIndicator extends Rect implements IGridVisualElement
{
private var caretIndicatorFill:SolidColorStroke ;
public function CaretIndicator()
{
super();
caretIndicatorFill = new SolidColorStroke();
this.stroke = caretIndicatorFill;
}
public function prepareGridVisualElement(grid:Grid, rowIndex:int, columnIndex:int):void
{
const dataGrid:DataGrid = grid.dataGrid;
if (!dataGrid)
return;
const color:uint = dataGrid.getStyle("caretColor");
caretIndicatorFill.color = color;
}
}
}
The same goes for SelectionIndicator and HoverIndicator, just don't forget to use SolidColor instead of solidColorStroke and the property fill or Rect instead of the property stroke.
Second Step
In the personalized datagrid, I added event listeners for the events gridClick, gridRollOver and gridMouseDown:
this.addEventListener(GridEvent.GRID_CLICK, gridClickHandler);
this.addEventListener(GridEvent.GRID_ROLL_OVER, gridEventHandler);
this.addEventListener(GridEvent.GRID_MOUSE_DOWN, mouse_down_handler);
And here are the event listeners:
protected function mouse_down_handler(event:GridEvent):void
{
if (_disableFlag)
{
if (event.item)
{
if (disabledRow(event.item))
{
event.grid.caretIndicator = null;
event.grid.selectionIndicator = null;
}
}
}
}
protected function gridClickHandler(event:GridEvent):void
{
if (_disableFlag)
{
if (event.item)
{
if (!disabledRow(event.item))
{
event.grid.selectionIndicator = new ClassFactory(SelectionIndicator);
event.grid.caretIndicator = new ClassFactory(CaretIndicator);
}
}
}
}
protected function gridEventHandler(event:GridEvent):void
{
if (_disableFlag)
{
if (event.item)
{
if(disabledRow(event.item))
{
event.grid.hoverIndicator = null;
}
else
{
event.grid.hoverIndicator = new ClassFactory(HoverIndicator);
}
}
}
}
You probably already guessed that _disableFlag and disabledRow are respectively a Boolean and a Function. Now the last step is the easiest:
Third Step
When you use the personalized dataGrid, if you want to disable the columns, you set _disableFlag to true and implement the disabledRow which returns true if a condition is met, and false if not
<custom:NinjaGrid id="ninja"
_disableFlag=true
creationComplete="ninja_trainingCompleteHandler(event)"/>
protected function ninja_trainingCompleteHandler(event:FlexEvent):void
{
ninja.disabledRow = beNinja;
}
private function beNinja(ninja:Object):Boolean
{
if (ninja.knowHowToWalkOnWater == true)
return true;
return false;
}
This solution doesn't work for the selection mode multiple rows, I found another solution for the multiple rows selection mode.
Second Solution
In this solution, I use only the HoverIndicator component, the event listener for the rollover event will remain the same. I don't use the event listener for the click event anymore, and in the mouse_down_handler function, I have now this code:
protected function mouse_down_handler(event:GridEvent):void
{
if(_disableFlag)
{
if (event.item)
{
if (!disabledRow(event.item))
{
if (!event.ctrlKey)
{
tempItem = event.item;
tempSelecteditems = new Vector.<Object>();
tempSelecteditems.push(tempItem);
}
else
{
if (tempSelecteditems.indexOf(event.item) < 0)
{
tempSelecteditems.push(event.item);
}
else
{
tempSelecteditems[tempSelecteditems.indexOf(event.item)] = null;
}
}
}
else
{
if (!event.ctrlKey)
{
selectedItem = tempItem;
tempSelecteditems = new Vector.<Object>();
}
else
{
if (tempSelecteditems.length)
{
selectedItems = tempSelecteditems;
}
else
{
selectedItem = tempItem;
}
}
}
}
}
}