Custom Components in Crafty.js - craftyjs

I am new to Crafty.js. I am currently working a 2D Top Down RPG using this frame work. I am trying to create a custom component that will contain the Unit's information such as Health, Mana, Name etc. My component's code are as follows:
Crafty.c("UnitInfo", {
init: function() {
this.bind("EnterFrame", function(){
console.log("Custom Component Test: " + this.message);
});
},
unitinfo: function(message) {
this.message = message;
}
});
Then I added this component to a unit which is in my case, an Enemy entity:
enemy1 = Crafty.e('Enemy, 2D, Canvas, Color, enemy_default, SpriteAnimation, Solid, Collision, UnitInfo')
.attr({x: (screenWidth/2) + 150, y: (screenHeight/2) + 150, w: spriteBaseW, h: spriteBaseH, curHealth: 100 })
.checkHits("Skill")
.unitinfo("Random Msg")
.bind("HitOn", function() {
var rndDmg = 0;
if(currentSkill==1) rndDmg = skillDamage.skill1;
else if(currentSkill==2) rndDmg = skillDamage.skill2;
else if(currentSkill==3) rndDmg = skillDamage.skill3;
else if(currentSkill==4) rndDmg = skillDamage.skill4;
rndDmg = randomRange(rndDmg-9,rndDmg);
this.curHealth -= rndDmg;
Crafty.e("2D, DOM, Text, Tween")
.attr({x: this.x, y: this.y})
.textColor("white")
.text(rndDmg)
.textFont({size: "20px"})
.tween({y: this.y-30, alpha: 0.0}, 2000)
.bind("TweenEnd", function(){
this.destroy();
});
if(this.curHealth<=0) {
//this.destroy();
//console.log("An Enemy was killed!!!!");
}else{
console.log("Enemy HP: " + this.curHealth + ", Damage Taken: " + rndDmg);
}
})
.bind("EnterFrame", function() {
enemyAI(this);
});
When I execute the code, it shows me this error:
console error
I believe that the unitinfo() is properly working as the "Random Msg" is appearing on the console logs and it is being executed every frame. I just don't understand why "this" is being recognized as undefined. Can someone help me on this?

This is because you're attempting to call bind on the result of unitinfo(), but that method doesn't return the entity.
This is a fundamental aspect of javascript, but it's definitely a gotcha when implementing custom components in Crafty. From the docs
Keep in mind that the method chaining technique (calling e.place().color()) is only possible because we explicitly return this from our custom method. Forgetting to do so can be a common source of errors, so keep that in mind if you get a hard-to-pin-down "method undefined" message.
i.e. when you define a method, you have to explicitly return this to use it in this manner.

Related

How to make a GUI to visually add Mattertags into a Matterport scene?

There are 2 examples in the Matterport SDK for Embeds documentation to show how to place Mattertags in a scene:
The Intersection Inspector which only allows you to see coordinates for placing a Mattertag where the cursor is if you wait a little bit ... Not very user friendly, you need to copy the coordinates manually in your program.
The Transient Tags Editor which enable you to interactively place multiple Mattertags visually, edit them and then to extract them easily in a JSON file ...
I was wondering how to reproduce the Transient Tags Editor visual UX as I would like to use it in an application.
Insert Mattertags into the model visually
The source code of the app of the Transient Tags Editor is privately hosted on github (Maybe because it doesn't run perfectly on Firefox?), unlike the source code of the Intersection Inspector which is publicly hosted on JSFiddle.
But the user friendliness of the Transient Tags Editor intrigued me and I wanted to understand the difference between the two tools Matterport SDK provides to find out Mattertags coordinates.
How the Intersection Inspector works
The Intersection Inspector uses a timer to display a button at the position of the Pointer when the user does not move the pointer for more than one second. The user can then click the button to see the Mattertag coordinates and copy them manually ...
To achieve that, it needs the current Camera position, which it obtains by observing the camera's pose property:
var poseCache;
mpSdk.Camera.pose.subscribe(function(pose) {
poseCache = pose;
});
Also, it needs the current Pointer position, which it obtains by observing the pointer's intersection property:
var intersectionCache;
mpSdk.Pointer.intersection.subscribe(function(intersection) {
intersectionCache = intersection;
intersectionCache.time = new Date().getTime();
button.style.display = 'none';
buttonDisplayed = false;
});
※ An intersection event is triggered the user moves the pointer, so we hide the button to make sure it is not displayed before the one second delay is over.
Then, a timer is set up using setInterval() to display the button at the right time:
setInterval(() => {
// ...
}, 16);
In the timer callback, we check wether all the conditions to display the button are met ...
First, check we have the information we need:
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
// ...
}, 16);
Then, check one second has elapsed since the last intersection event was received, or we wait the next tick to check again:
var delayBeforeShow = 1000;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
// ...
}
}, 16);
Finally, we check the button is not already being displayed:
var delayBeforeShow = 1000;
var buttonDisplayed = false;
setInterval(() => {
if (!intersectionCache || !poseCache) {
return;
}
const nextShow = intersectionCache.time + delayBeforeShow;
if (new Date().getTime() > nextShow) {
if (buttonDisplayed) {
return;
}
// ...
}
}, 16);
Once the conditions are met, we can display the button using Conversion.worldToScreen() to get the screen coordinate of the pointer :
// ...
setInterval(() => {
// ...
if (new Date().getTime() > nextShow) {
// ...
var size = {
w: iframe.clientWidth,
h: iframe.clientHeight,
};
var coord = mpSdk.Conversion.worldToScreen(intersectionCache.position, poseCache, size);
button.style.left = `${coord.x - 25}px`;
button.style.top = `${coord.y - 22}px`;
button.style.display = 'block';
buttonDisplayed = true;
}
}, 16);
The button is a simple HTML button hidden by default using display: none; and positioned relative to the body with position: absolute;.
When the user clicks the button, the current coordinates of the pointer are displayed in a <div> tag above the <iframe> and the button is hidden:
button.addEventListener('click', function() {
text.innerHTML = `position: ${pointToString(intersectionCache.position)}\nnormal: ${pointToString(intersectionCache.normal)}\nfloorId: ${intersectionCache.floorId}`;
button.style.display = 'none';
iframe.focus();
});
The coordinates are formatted using the following function:
function pointToString(point) {
var x = point.x.toFixed(3);
var y = point.y.toFixed(3);
var z = point.z.toFixed(3);
return `{ x: ${x}, y: ${y}, z: ${z} }`;
}
Now, let's see how the easier-to-use and user-friendlier Transient Tags Editor interface works ...
How the Transient Tag Editor works
The Intersection Inspector is enough if you just have a few __Mattertag__s to set permanently in a few models in your application. But if you need your users to set tags interactively in models, something like the Transient Tags Editor's GUI is a better starting point.
The main advantage of using the Transient Tags Editor is that you can see how the Mattertag will be displayed before creating it and! That allows you to place the tag precisely without trial and error ...
To add a tag, you must click on the "Place New Tag" button to enter the "add tag" mode, then you can place one new tag anywhere you want.
We will only focus on that aspect of the editor and produce a simplified code sample that only add tags:
As the user move a tag along the pointer when in "add tag" mode, the first step is to create a new tag and place it. Let's create a function for that using Mattertag.add():
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
Then we will have to place the tag at a position near the pointer, and update its position as the user moves the pointer, so let's create a function for that using Mattertag.editPosition():
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
As you can see the updateTagPos() function takes 3 parameters:
newPos: the new anchor position for the Mattertag.
newNorm: an optional new stem vector for the Mattertag.
scale: an optional new scale for the stem of the Mattertag.
To update the tag position as the user moves the pointer, let's observe the pointer's intersection property to call updateTagPos():
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
To place the new tag, the user simply clicks their mouse button, the Transient Tags Editor provides its own version of the document.activeElement method for intercepting clicks on the <iframe> (but does not work with Firefox so the editor use a quite complex workaround):
function focusDetect(){
const eventListener = window.addEventListener('blur', function() {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
setTimeout(function(){ window.focus(); }, 0);
}
window.removeEventListener('blur', eventListener );
});
}
But, we will use our version which works better with Firefox (But still stop working after the first click in Firefox for whatever reason):
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
Finally, let's the function that navigates to the new tag and opens its billboard, usingMattertag.navigateToTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
Simple Editor Code Sample
First, the complete JavaScript source code:
"use strict";
const sdkKey = "aaaaXaaaXaaaXaaaXaaaXaaa"
const modelSid = "iL4RdJqi2yK";
let iframe;
let tag;
document.addEventListener("DOMContentLoaded", () => {
iframe = document.querySelector('.showcase');
iframe.setAttribute('src', `https://my.matterport.com/show/?m=${modelSid}&help=0&play=1&qs=1&gt=0&hr=0`);
iframe.addEventListener('load', () => showcaseLoader(iframe));
});
function showcaseLoader(iframe){
try{
window.MP_SDK.connect(iframe, sdkKey, '3.10')
.then(loadedShowcaseHandler)
.catch(console.error);
} catch(e){
console.error(e);
}
}
function loadedShowcaseHandler(mpSdk){
addTag()
function placeTag(){
if(tag) mpSdk.Mattertag.navigateToTag(tag, mpSdk.Mattertag.Transition.INSTANT);
tag = null;
}
window.addEventListener('blur',function(){
window.setTimeout(function () {
if (document.activeElement === iframe) {
placeTag(); //function you want to call on click
window.focus()
addTag();
}
}, 0);
});
function updateTagPos(newPos, newNorm=undefined, scale=undefined){
if(!newPos) return;
if(!scale) scale = .33;
if(!newNorm) newNorm = {x: 0, y: 1, z: 0};
mpSdk.Mattertag.editPosition(tag, {
anchorPosition: newPos,
stemVector: {
x: scale * newNorm.x,
y: scale * newNorm.y,
z: scale * newNorm.z,
}
})
.catch(e =>{
console.error(e);
tag = null;
});
}
mpSdk.Pointer.intersection.subscribe(intersectionData => {
if(tag){
if(intersectionData.object === 'intersectedobject.model' || intersectionData.object === 'intersectedobject.sweep'){
updateTagPos(intersectionData.position, intersectionData.normal);
}
}
});
function addTag() {
if(!tag){
mpSdk.Mattertag.add([{
label: "Matterport Tag",
description: "",
anchorPosition: {x: 0, y: 0, z: 0},
stemVector: {x:0, y: 0, z: 0},
color: {r: 1, g: 0, b: 0},
}])
.then((sid) => {
tag = sid[0];
})
.catch( (e) => {
console.error(e);
})
}
}
} // loadedShowcaseHandler
The HTML source code:
<!DOCTYPE html>
<html>
<head>
<title>Transient Tag Editor</title>
<style>
#showcase {
width: 100%;
height: 100vh;
}
</style>
<script src="https://static.matterport.com/showcase-sdk/2.0.1-0-g64e7e88/sdk.js"></script>
<script src="/js/app-editor.js" type="text/javascript" defer></script>
</head>
<body>
<iframe id="showcase" frameborder="0" allowFullScreen allow="xr-spatial-tracking"></iframe>
</body>
</html>
It works!
Complete Code
The complete code for this sample and others is available on github:
github.com/loic-meister-guild/pj-matterport-sdk-2021-tutorial
See Also
Matterport SDK 2021 Tutorial
Node.js + Express Tutorial for 2021
How to detect a click event on a cross domain iframe

dojox.gfx - Maintaining gfx references for event connection

I have a simple program where I am GETing an svg file and then creating gfx shapes from rect elements within the svg as follows
function superCallback(shape){
shape.setFill("red");
}
dojo.ready(function() {
// Load image
dojo.xhrGet({
//handleAs: 'xml',
url: 'svg/demo.svg',
load: function(data) {
// Create the surface
var surface = dojox.gfx.createSurface("logoHolder", 2000, 1500);
var group = surface.createGroup();
var dom = dojox.xml.parser.parse(data);
rects = dom.getElementsByTagName("rect");
for (var idx = 0 ; idx < rects.length ; idx++) {
//alert('Coordinates ' + rects[idx].getAttribute("x") + ', ' + rects[idx].getAttribute("y"));
var rectangle = group.createRect({ x: parseFloat(rects[idx].getAttribute("x")),
y: parseFloat(rects[idx].getAttribute("y")),
width: parseFloat(rects[idx].getAttribute("width")),
height: parseFloat(rects[idx].getAttribute("height")) })
.setStroke({ style: "Solid" , width: "7" , color :"black"});
rectangle.connect("onclick",dojo.hitch(rectangle,function(e) {
superCallback(rectangle);
}));
}
});
});
As can be seen, I am using a local variable rectangle to create and store the gfx shape and then calling connect to attach an event to it.
The problem is that the event is attached to only the last gfx rect which is created in teh for loop because I guess the closure is executing in the context of the last rectangle reference always.
Need some help in fixing this so that all rects can be triggered by the event
Thanks in advance
Regards
Shyam

Latency issue with Primefaces overlayPanel - loads to lazy

I am using Primefaces 3.2 with jsf 2 and glassfish 3.1.2.
I have a p:dataTable of users containing avatars of the user. Whenever the user moves the mouse over the avatar a p:overlayPanel appears with more information (lazy loaded) on the user, and disappears when the user moves the cursor away - like this:
<p:overlayPanel for="avatar" dynamic="true" showEvent="mouseover" hideEvent="mouseout" ...>
This works very well - as long as the user is "slowhanded". Whenever an user moves the cursor fast above many avatars many of the overlayPanels stay visible.
For example when the user has the cursor over the position where user avatars are displayed and uses the scroll wheel of his mouse to scroll the usertable down or up.
I believe that the overlaypanel starts to load the information dynamically (dynamic="true") from the server when showEvent="mouseover" is dispatched and displays the overlaypanel after the response from the server arrives.
This way it is not possible to detect whether the cursor is already away when the overlaypanel becomes visible - so the hideEvent="mouseout" is never dispatched.
Is there a way to make the primefaces overlaypanel appear directly on mousover, showing a loading gif and update the content into the overlaypanel when the response comes from the server.
Is this a good appraoch or does anyone know any other way to solve this nasty problem?
Thanks Pete
As my first answer is already very long and contains valid information, I decided to open a new answer presenting my final approach.
Im now using Primefaces inheritance pattern making the code alot cleaner. Also I noticed that replacing/overwriting the whole bindEvents function isnt necessary, as we can remove the old event handlers. Finally this code fixs the latest issue experienced: A hide event before ajax arrival.
PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.OverlayPanel
.extend({
bindEvents : function() {
this._super();
var showEvent = this.cfg.showEvent + '.ui-overlay', hideEvent = this.cfg.hideEvent
+ '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(
showEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.timer = setTimeout(function() {
_self.hidden = false;
_self.show();
}, 300);
}).on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hidden = true;
_self.hide();
});
},
_show : function() {
if (!this.cfg.dynamic || !this.hidden) {
this._super();
}
}
});
Im sorry for the poor formatting: Eclipses fault ;)
Wow, finally after a long debuging session and testing various approaches i recognized that the problem isnt the ajax request but the event handlers itself:
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
if(_self.isVisible()) {
_self.hide();
}
});
As you can see, the widget is just hidden if its visible before. If your moving your mouse out too fast, now two things can happen:
The widget isnt visible at all
The animation is still going on
In this case the event is discarded and the panel stays visible. As animations are queued, one simply has to remove the if statement to fix the issue. I did this by replacing the whole bindEvents method:
PrimeFaces.widget.OverlayPanel.prototype.bindEvents = function() {
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent == this.cfg.hideEvent) {
var event = this.cfg.showEvent;
$(document).off(event, this.targetId).on(event, this.targetId, this, function(e) {
e.data.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlay',
hideEvent = this.cfg.hideEvent + '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
if(!_self.isVisible()) {
_self.show();
}
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.hide();
});
}
//enter key support for mousedown event
this.bindKeyEvents();
var _self = this;
//hide overlay when mousedown is at outside of overlay
$(document.body).bind('mousedown.ui-overlay', function (e) {
if(_self.jq.hasClass('ui-overlay-hidden')) {
return;
}
//do nothing on target mousedown
var target = $(e.target);
if(_self.target.is(target)||_self.target.has(target).length > 0) {
return;
}
//hide overlay if mousedown is on outside
var offset = _self.jq.offset();
if(e.pageX < offset.left ||
e.pageX > offset.left + _self.jq.outerWidth() ||
e.pageY < offset.top ||
e.pageY > offset.top + _self.jq.outerHeight()) {
_self.hide();
}
});
//Hide overlay on resize
var resizeNS = 'resize.' + this.id;
$(window).unbind(resizeNS).bind(resizeNS, function() {
if(_self.jq.hasClass('ui-overlay-visible')) {
_self.hide();
}
});
};
Execute this code on load and the issue should be gone.
As your replacing the js code nevertheless, you can use this oppurtunity to implement quite a nice feature. By using timeouts in the event handlers one can easily implement a little delay not just improving usability (no more thousands of popups appear) but also reducing network traffic:
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.timer = setTimeout( function(){
if(!_self.isVisible()) {
_self.show();
}
}, 300);
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hide();
});
Ofcourse you can use a global variable to control the delay time. If you want a more flexible approach youll have to overwrite the encodeScript method in the OverlayPanelRender to transmit an additional property. You could access it then with _self.cfg.delay. Notice though that youll have to replace the component model OverlayPanel too providing it with an extra attribute.
At the same time I thank you for this brilliant solution I take the opportunity to update it for Primefaces 5.2. In our application the code broke after that upgrade.
Follows the updated code for Primefaces 5.2:
PrimeFaces.widget.OverlayPanel.prototype.bindTargetEvents = function() {
var $this = this;
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent === this.cfg.hideEvent) {
var event = this.cfg.showEvent;
this.target.on(event, function(e) {
$this.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';
this.target
.off(showEvent + ' ' + hideEvent)
.on(showEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
$('.ui-overlaypanel').hide();
$this.hidden = false;
$this.show();
}, 500);
})
.on(hideEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
// don't hide if hovering overlay
if(! $this.jq.is(":hover")) {
$this.hide();
}
}, 100);
});
}
$this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel').on('keydown.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
e.preventDefault();
}
})
.on('keyup.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
$this.toggle();
e.preventDefault();
}
});
};
I also added an extra feature which allows the user to move the mouse over the overlay without hiding it. It should hide when you move the mouse out of it then which I accomplished through:
<p:overlayPanel .... onShow="onShowOverlayPanel(this)" ...>
function onShowOverlayPanel(ovr) {
ovr.jq.on("mouseleave", function(e) {
ovr.jq.hide();
});
}
Hope you enjoy!
It's been a long time, but in case anyone bumps into this problem, a showDelay attribute was added to the overlayPanel to solve this problem starting from Primefaces 6.2. However, it is not in the official documentation for some reason.

Problems with addFrameScript on an instance

I'm trying to add a frame script via:
private function addFrameScript(mc:MovieClip, label:String, func:Function, arg:*):void {
var labels:Array = mc.currentLabels;
var i:int = labels.length;
while (--i > -1) {
if (FrameLabel(labels[i]).name == label) {
mc.addFrameScript(FrameLabel(labels[i]).frame-1, func(arg));
return;
}
}
trace("WARNING: The label '" + label + "' does not exist in the MovieClip '" + mc.name + "'");
}
private function dispatchFrameEvent(labelName:String):void {
dispatchEvent(new Event(labelName, true));
}
addFrameScript(instanceName, "end", dispatchFrameEvent, "end");
But it doesn't seem to be adding and I can't figure out why. Normally I add Frame scripts to the main timeline, but not to an instance on the timeline (as in the example above).
Clearly there's something I'm missing...is there a way to trace out my frame script to see if it's even being added?
The problem here is that addFrameScript functions being added take no arguments. so whatever func you are passing in must must return a function with no arguments
so your dispatchFrameEvent should look like this
private function dispatchFrameEvent(labelName:String):Function {
return function func():void{
dispatchEvent(new Event(labelName, true));
}
}

How to wait for 3 seconds in ActionScript 2 or 3?

Is there any way to implement waiting for, say, 3 seconds in ActionScript, but to stay within same function? I have looked setInterval, setTimeOut and similar functions, but what I really need is this:
public function foo(param1, param2, param3) {
//do something here
//wait for 3 seconds
//3 seconds have passed, now do something more
}
In case you wonder why I need this - it is a legal requirement, and no, I can't change it.
Use the Timer to call a function after 3 seconds.
var timer:Timer = new Timer(3000);
timer.addEventListener(TimerEvent.TIMER, callback); // will call callback()
timer.start();
To do this properly, you should create the timer as an instance variable so you can remove the listener and the timer instance when the function is called, to avoid leaks.
class Test {
private var timer:Timer = new Timer(3000);
public function foo(param1:int, param2:int, param3:int):void {
// do something here
timer.addEventListener(TimerEvent.TIMER, fooPartTwo);
timer.start();
}
private function fooPartTwo(event:TimerEvent):void {
timer.removeEventListener(TimerEvent.TIMER, fooPartTwo);
timer = null;
// 3 seconds have passed, now do something more
}
}
You could also use another function inside your foo function and retain scope, so you don't need to pass variables around.
function foo(param1:int, param2:int, param3:int):void {
var x:int = 2; // you can use variables as you would normally
// do something here
var timer:Timer = new Timer(3000);
var afterWaiting:Function = function(event:TimerEvent):void {
timer.removeEventListener(TimerEvent.TIMER, afterWaiting);
timer = null;
// 3 seconds have passed, now do something more
// the scope is retained and you can still refer to the variables you
// used earlier
x += 2;
}
timer.addEventListener(TimerEvent.TIMER, afterWaiting);
timer.start();
}
For AS3 use Radu's answer.
For AS2 use the setInterval function like so:
var timer = setInterval(function, 3000, param1, param2);
function (param1, param2) {
// your function here
clearInterval(timer);
}
You can also use delayedCall, from TweenMax. IMHO, it's the sharpest way to do that if you are familiar to TweenMax family.
TweenMax.delayedCall(1, myFunction, ["param1", 2]);
function myFunction(param1:String, param2:Number):void
{
trace("called myFunction and passed params: " + param1 + ", " + param2);
}
In your case, using a anonymous function:
public function foo(param1, param2, param3) {
//do something here
trace("I gonna wait 3 seconds");
TweenMax.delayedCall(3, function()
{
trace("3 seconds have passed");
});
}
why you are doing some confused ways instead of doing the right way?
there is a method named:"setTimeout()";
setTimeout(myFunction,3000);
myFunction is the function you want to call after the period.and 3000 is the period you want to wait(as miliseconds).
you don't need to set then clear interval, or make a timer with one repeat count or do sth else with more trouble☺.
There is no Sleep in ActionScript. But there are other ways to achieve the same thing without having all your code in a single function and wait within that function a specific amount of time.
You can easily have your code in two functions and call the 2nd one after a specific timeout you set in your 1st function.
THIS IS NOT WITHIN ONE FUNCTION - ANSWERS: "How to wait for X seconds in AS2 & 3"
...without using setInterval or clearInterval.
The answers posted above are much faster and easier to use. I posted this here, just in case...
Sometimes you may not be able to use set/clearInterval or other methods based on development restrictions. Here is a way to make a delay happen without using those methods.
AS2 - If you copy/paste the code below to your timeline, make sure to add two movie clips to the stage, btnTest and btnGlowTest (include like instance names). Make "btnGlowTest" larger, a different color, & behind "btnTest" (to simulate a glow and a button, respectively).
Compile and check the output panel for the trace statements to see how the code is working. Click on btnTest - btnGlowTest will then become visible throughout the duration of the delay, (just for visual representation).
I have an onEnterFrame countdown timer in here as well, (demos stopping/switching timers).
If you want the delay/glow to be longer - increase the glowGameTime number. Change the names to fit your own needs and/or apply the logic differently.
var startTime:Number = 0;
var currentTime:Number = 0;
var mainTime:Number = 5;//"game" time on enter frame
var glowStartTime:Number = 0;
var glowCurrentTime:Number = 0;
var glowGameTime:Number = 1.8;//"delayed" time on press
btnGlowTest._visible = false;
this.onEnterFrame = TimerFunction;
startTime = getTimer();
function TimerFunction()
{
currentTime = getTimer();
var timeLeft:Number = mainTime - ((currentTime - startTime)/1000);
timeLeft = Math.floor(timeLeft);
trace("timeLeft = " + timeLeft);
if(timeLeft <= 0)
{
trace("time's up...3 bucks off");
//...do stuff here
btnGlowTest._visible = false;//just for show
btnTest._visible = false;//just for show
StopTime();
}
}
function glowTimerFunction()
{
glowCurrentTime = getTimer();
var glowTimeLeft:Number = glowGameTime - ((glowCurrentTime - glowStartTime)/1000);
glowTimeLeft = Math.floor(glowTimeLeft);
//trace("glowTimeleft = " + glowTimeLeft);
if(glowTimeLeft <= 0)
{
trace("TIME DELAY COMPLETE!");
//...do stuff here
btnGlowTest._visible = false;//just for show
btnTest._visible = false;//just for show
StopTime();
}
}
btnTest.onPress = function()
{
trace("onPress");
btnGlowTest._visible = true;
StopTime();
GlowTime();
}
function GlowTime()
{
trace("GlowTime Function");
this.onEnterFrame = glowTimerFunction;
glowStartTime = getTimer();
}
function StopTime()
{
trace(">>--StopTime--<<");
delete this.onEnterFrame;
}
AS3 - Below is the code from above setup to run in AS3. There are different ways to accomplish similar results, yet based on the project scope, these are the methods that were used in order to get things functioning properly.
If you copy/paste the code below to your timeline, make sure to add two movie clips to the stage, btnTest and btnGlowTest (include like instance names). Make "btnGlowTest" larger, a different color, & behind "btnTest" (to simulate a glow and a button, respectively).
Compile and check the output panel for the trace statements to see how the code is working. Click on btnTest - btnGlowTest will then become visible throughout the duration of the delay, (just for visual representation).
If you want the delay/glow to be longer - increase the GlowTimer:Timer number, (currently set to 950). Change the names to fit your own needs and/or apply the logic differently.
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
var startTime:Number = 0;
var currentTime:Number = 0;
var gameTime:Number = 4;//"game" time on enter frame
var GlowTimer:Timer = new Timer(950,0);//"delayed" time on press
btnGlowTest.visible = false;
GlowTimer.addEventListener(TimerEvent.TIMER, GlowTimeListener, false, 0, true);
btnTest.addEventListener(MouseEvent.MOUSE_DOWN, btnTestPressed, false, 0, true);
addEventListener(Event.ENTER_FRAME,TimerFunction, false, 0, true);
startTime = getTimer();
function TimerFunction(event:Event)
{
currentTime = getTimer();
var timeLeft:Number = gameTime - ((currentTime - startTime)/1000);
timeLeft = Math.floor(timeLeft);
trace("timeLeft = " + timeLeft);
if(timeLeft <= 0)
{
trace("time's up, 3 bucks off");
StopTime();
}
}
function GlowTimeListener (e:TimerEvent):void
{
trace("TIME DELAY COMPLETE!");
StopTime();
}
function btnTestPressed(e:MouseEvent)
{
trace("PRESSED");
removeEventListener(Event.ENTER_FRAME, TimerFunction);
btnGlowTest.visible = true;
GlowTimer.start();
}
function StopTime()
{
trace(">>--Stop Time--<<");
btnGlowTest.visible = false;//just for show
btnTest.visible = false;//just for show
GlowTimer.stop();
removeEventListener(TimerEvent.TIMER, GlowTimeListener);
removeEventListener(Event.ENTER_FRAME, TimerFunction);
}

Resources