First of all, my question is basically the same as this one:
How to drag an image to move it smoothly on screen, with ActionScript?
I want my dragged Sprites to keep up with the mouse on-screen, smoothly, without lagging behind.
And I notice that in the old ActionScript 3 Cookbook from way-back-when that they used a similar solution for their DraggableSprite as was used in the above link. Namely, use the stage instance to listen for the MouseMove event and then read from the event.stageX and stageY properties.
I've done that.
But my Sprite still doesn't stay locked with the mouse cursor. It lags behind. I feel like I must be missing something. However, if the solution posted above (ie listen for stage's MouseMove and use event.stageX/Y) is still current and the problem I'm describing should not be occurring, please also let me know. Even though it's not supposed to work, I've tried event.updateAfterEvent() and it also doesn't seem to have any positive effect.
Any help or advice would be greatly appreciated.
Here's a simple example of how I've written the handlers. It should work as-is if pasted into a new project.
I should also add that I'm compiling this as a desktop application using Adobe AIR. Would the run time be a factor???
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
[SWF(width="1280", height="720", frameRate="30")]
public class test_drag extends Sprite {
private var testDragSprite:TestDragSprite;
public function test_drag() {
super();
graphics.clear();
graphics.beginFill(0x0000FF);
graphics.drawRect(0, 0, 1280, 720);
graphics.endFill();
testDragSprite = new TestDragSprite();
addChild(testDragSprite);
testDragSprite.addEventListener(MouseEvent.MOUSE_DOWN, testDragSprite_mouseHandler);
testDragSprite.addEventListener(MouseEvent.MOUSE_UP, testDragSprite_mouseHandler);
}
private function testDragSprite_mouseHandler(e:MouseEvent):void {
switch (e.type) {
case MouseEvent.MOUSE_DOWN: {
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
break;
}
case MouseEvent.MOUSE_UP: {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
break;
}
}
}
private function mouseMoveHandler(e:MouseEvent):void {
//-20 to keep the sprite centered on the mouse
testDragSprite.x = e.stageX - 20;
testDragSprite.y = e.stageY - 20;
//e.updateAfterEvent(); //strange effect, but doesn't solve the problem.
}
}
}
import flash.display.Sprite;
internal class TestDragSprite extends Sprite {
public function TestDragSprite() {
super();
graphics.lineStyle(1, 0xDDDDDD);
graphics.beginFill(0xFF0000);
graphics.drawRoundRect(0, 0, 40, 40, 12);
graphics.endFill();
}
}
There is always going to be a little lag, but:
The first two suggestions will make the most noticeable change to your code/performance.
Enable hardware graphics acceleration; edit your AIR application
descriptor file (xml) and set renderMode to Direct (or GPU). Consult
the Adobe Air help for details.
<!-- The render mode for the app (either auto, cpu, gpu, or direct). Optional. Default auto -->
<renderMode>direct</renderMode>
Use startDrag and endDrag to bypass your manual assignments in your mouseMoveHandler.
Replace :
testDragSprite.addEventListener(MouseEvent.MOUSE_DOWN, testDragSprite_mouseHandler);
testDragSprite.addEventListener(MouseEvent.MOUSE_UP, testDragSprite_mouseHandler);
With :
testDragSprite.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
testDragSprite.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
Add the new handlers:
private function mouseDown(e:MouseEvent):void {
testDragSprite.startDrag();
}
private function mouseUp(e:MouseEvent):void {
testDragSprite.stopDrag();
}
Increase your frame rate, but watch your CPU usage as your application/game becomes more complicated as you may need to lower it to allow your game logic enough time to complete between frames (otherwise you end up with what is called long frames, you can use the free Adobe Scout profiling tool to watch for these and lots of other things).
Add a frame rate HUD to your display so you can monitor when your actual framerate is lower than the requested framerate (this is for debugging)
If your game is 2d based, consider using the open-source Starling framework as all content is rendered directly by the GPU vs Flash's standard display list objects.
Just try the member function startDrag.
The Feathers UI library includes a drag-and-drop module. The answers above all use the 'old' display list which is not mobile-ready. If you want your code to work cross-platform you need a GPU framework (most popular is Starling on which Feathers is based, as well as most Adobe AIR games and Apps).
Related
I am creating an app where when a button is pressed a very large picture is added to the stage (it is larger than the screen but can be dragged around by the user)
When the button is pressed the picture (well, movieClip) does come up and it able to be dragged fine, buttons inside it work.
The problem though is that there is a pause of about 6 seconds between the button press and the image appearing. I am using one .fla file for publishing and compiling (let's just call it Main.fla for now), and another one to hold all the graphics. The graphics are then added with this embed command:
[Embed (source = "assets/graphic.swf", symbol = "Content")]
private var Content:Class;
private var _content:*;
I have these lines where all the variables are declared (in between the class definition and the constructor function) I was under the impression that embedding it like this was equivalent to loading it at compile time. Is this true? What could be causing this lag when the button is pressed?
If I can't cut out the lag, another idea I had was to make some spinning circle or something to tell the user, "hey, don't worry. It's loading!"
If the slowness is at addChild you can add the asset to the stage much earlier and set it's visiblility to false, then when the button is clicked set it back to true. Obviously this is a small hack but might be sufficient for what you are doing.
var asset:MovieClip;
private function init():void
{
asset = new Content();
assset.visible = false;
addChild(asset);
button.addEventListener(MouseEvent.CLICK, onMouseClick);
}
private function onMouseClick(e:MouseEvent):void
{
asset.visible = true;
}
Embedding your SWF is probably not what is causing the delay.. or rather it would not likely be better if you imported the SWF into your FLA. Is this on a device? Chances are you would either have to devise a different way of loading your asset, or be satisfied with a loading animation.
If the main K size is coming from a large image, you could consider loading it in segments, starting with the part that is initially visible.
Is it possible to do completely smooth scrolling in Flash (ActionScript 3)? In the following test I am creating a bitmap consisting of random noise, then moving it to the left periodically. I have no heavy tasks running in the background. What I am looking for is smoothness that would be on par with my Amiga 500 from 1987 :-)
package {
import flash.display.*;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class Game extends Sprite {
var noiseBitmap;
public function Game() {
var noiseBitmapData = new BitmapData(stage.stageWidth * 3, stage.stageHeight);
noiseBitmapData.noise(0, 0, 255, 7, true);
noiseBitmap = new Bitmap(noiseBitmapData);
addChild(noiseBitmap);
var t = new Timer(1000/30, 999999);
t.addEventListener("timer", function (e:TimerEvent) {
noiseBitmap.x--;
});
t.start();
}
}
}
The "rendering code" takes <1 millisecond to run on my computer (2.4 GHz Mac), but still the movement will occasionally get stuck for a frame or two, making the movement appear jerky.
FPS is set to 30 in Flash. I have tried running it both using "test movie" and in the browser (Chrome). Target is Flash Player 11.2. I have also tried calling e.updateAfterEvent() to force a redraw. I have Also played around setting the delay and FPS to slightly different values, but no improvement.
This is different from not smooth scrolling in AS3 because I am already using a BitmapData. Also I have tried using the ENTER_FRAME event instead of a timer as suggested in a reply to that question, but it did not help.
When using a timer, you will not be exactly synced with the frame rate. As you mention, the frame rate fluctuates a little making your timer sometimes fire twice during one frame or skipping a frame. To be sure to be more in sync with the frame rate, you should listen to the Event.ENTER_FRAME event.
An example:
this.addEventListener(Event.ENTER_FRAME, updateFrame);
⋮
function updateFrame(e:Event):void {
noiseBitmap.x--;
}
I suggest you try out Greensock's TweenLite. It is a highly optimized engine to do all sorts of tweening through code and is available for AS2 and AS3. You can find it here.
Walkietokyo's solution is still frame based and would not eliminate the issues you run into. Instead use time-based animation (which TweenLite actually implements). For more info refer to this article.
I have developed an app in AS3 and Starling to be ported to IOS. I have updated the Default.png image and this works great however my app takes a while to load and a black screen is shown for about 3-4 seconds.
I have looked everywhere for a solution but couldn't find any that work. Does someone have a working solution?
Many thanks
I'm not sure if there is a neater solution at the moment but what I do is add a bitmap of the default screen to the native flash stage. Then when Starling is ready I remove the bitmap.
So before you instantiate Starling, add the bitmap image to the stage (this will be the Flash stage)
public static var _splash:Bitmap;
//load or embed your bitmap//
addChild(_splash);
Then instantiate and start Starling. e.g.
myStarling = new Starling(Main, stage, null, null, Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
myStarling.stage3D.addEventListener(starling.events.Event.CONTEXT3D_CREATE, function(e:flash.events.Event):void {
// Starling is ready!
myStarling.start();
});
In your root Starling class (in this example it's Main), use an ADDED_TO_STAGE listener and when this is triggered, remove the bitmap.
public function Main() {
addEventListener(starling.events.Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded ( e:starling.events.Event ):void {
StartUp._splash.parent.removeChild(StartUp._splash);
StartUp._splash = null;
}
In the example above the root document class is called 'StartUp'.
As described by docs there is Default.png used as splash screen in iOS.
I am looking for a way to make a Flash movie clip (animation, like the ones created with Flash Pro CS), but purely in as3 - so I can import them into Prezi.
I have done a lot of as3 programming in Flash Builder with Flex projects and I have no background in how MovieClips work.
What I have already tried is extending a MovieClip class and trying to base the animation on Timers, this failed so I tried with ENTER_FRAME event (because flash animations are based on frames - so I thought...). But all this fails, only graphics drawn in the constructor are displayed - no animation happens. (As I wrote in the first paragraph I am testing this importing the swf into Prezi, opening it in a browser works as expected)
Is there any way to do it? Like listening to specific events?
Give sprite sheet a try. It's the best solution for animation in AS3, and also pretty simple to implement. for changing drawing, there are Timer and ENTER_FRAME event to do this.
Funny thing happened. I wanted to show you a sample code I was trying out (I already tried Sprite with ENTER_FRAME), that was not working. By accident I found a solution. It looks like you need to draw something in the first frame, or else the other frames won't work (in Prezi at least).
So here is the working code:
public class PreziTest extends Sprite{
private var radius:uint = 10;
public function PreziTest(){
addEventListener(Event.ENTER_FRAME, onEnterFrame);
onEnterFrame(null); // WITHOUT THIS IT WON'T WORK - YOU NEED TO DRAW SOMTHING IN THE FIRST FRAME
}
private function onEnterFrame(event:Event):void{
radius += 10;
if(radius > 200)
radius = 10;
graphics.clear();
graphics.beginFill(0xff0000);
graphics.drawCircle(radius, radius, radius);
}
}
Thanks for all your help!
1.How to load image resources? And in which case we should use "[Embed]" to insert resources?
2.which technology can improve the performance of the game flash game development ?
Use loaders to access external images, like this:
private function init():void
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoaderCompleteEvent);
loader.load(new UrlRequest("someImage.jpg"));
addChild(loader);
}
private function handleLoaderCompleteEvent():void
{
//Do something.
}
regarding the technologies: if you want some nice animations in your game, use the TweenLite-library, which is awesome for animations. If you want to use 3D, use Papervision3D or away3D, but that requires some studying and getting to know the libraries, as where TweenLite is easily accessible and used.
Doing more with bitmaps, and in some cases, it might help to compile computationally intensive routines from C code using Alchemy (for collision detection and physics issues, especially).
More than anything else though, you will benefit from using the Flash Builder (or 3rd party) profiler, and other forms of benchmarking to figure out what you need to improve on. There is no substitute for knowing the limitations of your platform and having a nice bag-o-tricks from experience.
I find that it all comes down to rendering in the end.. The more vector graphics you use the slower it gets. It makes a ridiculous difference for me if I either move to sprite sheets, or create a class that uses a single MovieClip as a resource for multiple BitmapData objects to draw from.
What I mean is: Say you have a MovieClip for a game enemy like a ship or something. Firstly, create one instance of the MovieClip somewhere that is easily accessible. From here, all of your Ship objects could extend Bitmap. Another feature would be maybe a rendering controller that applies rotation and sets the frame of your ship movieclip. From here, Ship could work similar to this:
public class Ship extends Bitmap
{
/**
* Constructor
*/
public function Ship()
{
/**
* 1. specify the source movieclip thats been set up in RenderClassThing
* 2. set the rotation to 45 degrees
* 3. set frame of source movieclip to 4
*/
RenderClassThing.prepare(shipMC, 45, 4);
// applies bitmap
bitmapData = RenderClassThing.getBitmapGraphics(shipMc);
}
}
This will make a massive difference if you use a lot of vector graphics.