Make dynamically loaded images ease in and out using actionscript - actionscript

I have 4 buttons and 4 images when I click a button a different image appears. I want the images to ease in and out after each button click. I have tried the code below but can't seem to figure it out.
home_btn.addEventListener(MouseEvent.CLICK, homeClick);
function homeClick(e:MouseEvent):void{
backgroundimg.source = "images/bluebg.jpg";
var bluebgTween:Tween = new Tween(backgroundimg, "alpha", Strong.easeIn, 1, 0, 3, true);
}

If fading in then fading out, set the initial alpha of backgroundimg to 0.
backgroundimg.alpha = 0;
You can then fade in on the CLICK event. Note that I've swapped the begin and finish values for the Tween.
Once the tween has finished, the yoyo() method can then be used to fade out again.
home_btn.addEventListener(MouseEvent.CLICK, homeClick);
function homeClick(e:MouseEvent):void{
backgroundimg.source = "images/bluebg.jpg";
var bluebgTween:Tween = new Tween(backgroundimg, "alpha", Strong.easeIn, 0, 1, 3, true);
bluebgTween.addEventListener(TweenEvent.MOTION_FINISH, onFinish);
function onFinish(te:TweenEvent):void {
bluebgTween.yoyo();
bluebgTween.removeEventListener(TweenEvent.MOTION_FINISH, onFinish);
}
}
I used these sites as a reference for this:
http://www.republicofcode.com/tutorials/flash/as3tweenclass/
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/fl/transitions/Tween.html

Related

Why are my UI elements not resetting correctly after being animated/scaled off screen?

So I'll give as much information about this project as I can right up front. Here is an image of a section of the storyboard that is relevant to the issue:
And here is the flow of the code:
1) A user plays the game. This scrambles up the emoji that are displayed and will eventually hide all of the emoji on the right side.
2) When someone wins the game, it calls
performSegue(withIdentifier: "ShowWinScreenSegue", sender: self)
Which will perform the segue the red arrow is pointing to. This segue is a modal segue, over current content, cross dissolve.
3) Stuff goes on here, and then I try to get back to the game screen so the user can play another game. Here is my current code for that
// self.delegate is the GameController that called the segue
// it's set somewhere else in the code so I can call these reset functions
GameController.gs = GameState()
guard let d = self.delegate else {
return
}
d.resetGameToMatchState()
dismiss(animated: true, completion: {
print("Modal dismiss completed")
GameController.gs = GameState()
self.delegate?.resetGameToMatchState()
})
So here's where the issue is. You can see that I have to call delegate?.resetGameToMatchState() twice for anything to actually happen. If I remove the top one, nothing happens when I call the second one and vice-versa. What makes this so annoying is that the user will see a weird jump where all the ui goes from the old state to the new state because it's updating so late and spastically.
What I've tried so far
So this whole issue has made me really confused on how the UI system works.
My first thought was that maybe the function is trying to update the UI in a thread that's executing too early for the UI thread. So I put the whole body of resetGameToMatchState in a DispatchQueue.main.async call. This didn't do anything.
Then I thought that it was working before because when the WinScreenSegue was being dismissed before (when it was a "show" segue) it was calling GameController's ViewDidAppear. I tried manually calling this function in the dismiss callback, but that didn't work either and feels really hacky.
And now I'm stuck :( Any help would be totally appreciated. Even if it's just a little info that can clear up how the UI system works.
Here is my resetGameToMatchState():
//reset all emoji labels
func resetGameToMatchState() {
DispatchQueue.main.async {
let tier = GameController.gs.tier
var i = 0
for emoji in self.currentEmojiLabels! {
emoji.frame = self.currentEmojiLabelInitFrames[i]
emoji.isHidden = false
emoji.transform = CGAffineTransform(scaleX: 1, y: 1);
i+=1
}
i=0
for emoji in self.goalEmojiLabels! {
emoji.frame = self.goalEmojiLabelInitFrames[i]
emoji.isHidden = false
emoji.transform = CGAffineTransform(scaleX: 1, y: 1);
i+=1
}
//match state
for i in 1...4 {
if GameController.gs.currentEmojis[i] == GameController.gs.goalEmojis[i] {
self.currentEmojiLabels?.findByTag(tag: i)?.isHidden = true
}
}
//reset highlight
let f = self.highlightBarInitFrame
let currentLabel = self.goalEmojiLabels?.findByTag(tag: tier)
let newSize = CGRect(x: f.origin.x, y: (currentLabel?.frame.origin.y)!, width: f.width, height: (currentLabel?.frame.height)! )
self.highlightBarImageView.frame = newSize
//update taps
self.updateTapUI()
//update goal and current emojis to show what the current goal/current selected emoji is
self.updateGoalEmojiLabels()
self.updateCurrentEmojiLabels()
}
}
UPDATE
So I just found this out. The only thing that isn't working when I try to reset the UI is resetting the right side emoji to their original positions. What I do is at the start of the app (in viewDidLoad) I run this:
for emoji in currentEmojiLabels! {
currentEmojiLabelInitFrames.append(emoji.frame)
}
This saves their original positions to be used later. I do this because I animate them to the side of the screen before hiding them.
Now when I want to reset their positions, I do this:
var i = 0
for emoji in self.currentEmojiLabels! {
emoji.frame = self.currentEmojiLabelInitFrames[i]
emoji.isHidden = false
emoji.transform = CGAffineTransform(scaleX: 1, y: 1);
i+=1
}
this should set them all to their original frame and scale, but it doesn't set the position correctly. It DOES reset the scale though. What's weird is that I can see a tiny bit of one of the emoji off to the left of the screen and when they animate, they animate from far off on the left. I'm trying to think of why the frames are so off...
UPDATE 2
So I tried changing the frame reset code to this:
emoji.frame = CGRect(x: 25, y: 25, width: 25, height: 25)
Which I thought should reset them correctly to the top left, but it STILL shoves them off to the left. This should prove that the currentEmojiLabelInitFrames are not the issue and that it has something to do with when I'm setting them. Maybe the constraints are getting reset or messed up?
Your first screen, GameController, should receive a viewWillAppear callback from UIKit when the modal WinScreenController is being dismissed.
So its resetGameToMatchState function could simply set a property to true, then your existing resetGameToMatchState could move into viewWillAppear, checking first if the property is being set.
var resetNeeded: Bool = false
func resetGameToMatchState() {
resetNeeded = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// reset code here
}
TLDR; Reset an element's scale BEFORE resetting it's frame or else it will scale/position incorrectly
Finally figured this out. Here's a bit more background. When an emoji is animated off the screen, this is called:
UIView.animate(withDuration: 1.5, animations: {
let newFrame = self.profileButton.frame
prevLabel?.frame = newFrame
prevLabel?.transform = CGAffineTransform(scaleX: 0.1, y: 0.1);
}) { (finished) in
prevLabel?.isHidden = true
}
So this sets the frame to the top left of the screen and then scales it down. What I didn't realize is that when I want to reset the element, I NEED to set the scale back to normal before setting the frame. Here is the new reset code:
for emoji in self.currentEmojiLabels! {
emoji.transform = CGAffineTransform(scaleX: 1, y: 1) //this needs to be first
emoji.frame = self.currentEmojiLabelInitFrames[i] //this needs to be after the scale
emoji.isHidden = false
i+=1
}

Action not working correctly in SpriteKit

I'm new to iOS programing and I'm experimenting to learn trying to create a game in swift using Sprite Kit.
What I'm trying to achieve is having a constant flow of blocks being created and moving rightwards on the screen.
I start by creating a set which contains all the initial blocks, then an action "constant movement" is added to each one, which makes them move slowly to the right. What I'm having trouble is adding new blocks to the screen.
The last column of blocks has an "isLast" boolean set to true, when it passes a certain threshold it is supposed to switch to false and add a new column of blocks to the set which now have "isLast" set to true.
Each block in the set has the "constantMovement" action added which makes them move slowly to the right, the new blocks have it added as well, but they don't work as the original ones.
Not all of the move, even tho if I print "hasActions()" it says they do, and the ones that do move stop doing so when they get to the middle of the screen. I have no idea why this happens, can somebody experienced give me a hint please?
This is the update function:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let constantMovement = SKAction.moveByX(-1, y: 0, duration: 10);
background.runAction(SKAction.repeatActionForever(constantMovement));
let removeBlock = SKAction.removeFromParent();
let frame = self.frame;
var currentBlockSprite:SKSpriteNode;
var newBlock: Block;
for block in blocks {
currentBlockSprite = block.sprite!;
currentBlockSprite.runAction(constantMovement);
if(block.column == NumColumns - 1) {
block.isLast = true;
}
if(block.isNew) {
println("position \(currentBlockSprite.position.x) has actions \(currentBlockSprite.hasActions())");
}
if(block.isLast && currentBlockSprite.position.x < frame.maxX - 50) {
println("the block that hits is " + block.description);
println("HITS AT \(currentBlockSprite.position.x)");
block.isLast = false;
for row in 0..<NumRows {
newBlock = Block(column: NumColumns - 1, row: row, blockType: BlockType.random(), isLast: true, isNew: true);
blocks.addElement(newBlock);
addBlockSprite(newBlock);
println("new block: " + newBlock.description + "position \(newBlock.sprite?.position.x)");
}
}
if(currentBlockSprite.position.x < frame.minX) {
currentBlockSprite.runAction(removeBlock);
blocks.removeElement(block);
}
}
}
My whole project is in here: https://github.com/thanniaB/JumpingGame/tree/master/Experimenting
but keep in mind that since I'm new to this it might be full of cringeworthy bad practices.
I would remove any SKAction code from the update function as that's kind of a bad idea. Instead I would just apply the SKAction when you add your block sprite to the scene, like this.
func addBlockSprite(block: Block) {
let blockSprite = SKSpriteNode(imageNamed: "block");
blockSprite.position = pointForColumn(block.column, row:block.row);
if(block.blockType != BlockType.Empty) {
addChild(blockSprite);
let constantMovement = SKAction.moveByX(-10, y: 0, duration: 1)
var currentBlockSprite:SKSpriteNode
let checkPosition = SKAction.runBlock({ () -> Void in
if(blockSprite.position.x < -512){
blockSprite.removeAllActions()
blockSprite.removeFromParent()
}
})
let movementSequence = SKAction.sequence([constantMovement, checkPosition])
let constantlyCheckPosition = SKAction.repeatActionForever(movementSequence)
blockSprite.runAction(constantlyCheckPosition)
}
block.sprite = blockSprite;
}
That would then allow you to simply add a new block whenever you see fit and it will have the appropriate action when it's added.
I've used 512 as thats the size of the iPhone 5 screen but you could swap this out for another screen size or what would be better would be a variable that dynamically reflects the screen size.

stop fade in out on a movieclip actionscript 2

I'm trying to stop fading in/out my movieclip.
I'll explain: I've integrated my swf in an HTML page with a dropdown list. When i choose an item from this list a javascript function it's called.This function execute a callback to a function in my swf file that fade in/out an image drawn at runtime (depending on the item selected in the dropdown list). When I choose another element I want the previuos item stops fading and the new starts.
This is my fading function:
function fadeIn(h){
if (eval(h)._alpha<100) {
eval(h)._alpha += 20;
}
else {
clearInterval(fadeInterval);
setTimeout(startOut, 500, h);
}
}
function fadeOut(h) {
if (eval(h)._alpha>0) {
eval(h)._alpha -= 20;
} else {
clearInterval(fadeInterval);
setTimeout(startIn, 100, h);
}
}
function startOut(h) {
fadeInterval = setInterval(fadeOut, 1, h);
}
function startIn(h){
fadeInterval = setInterval(fadeIn, 1, h);
}
function flashing(h){
var bname;
bname = "planGroup.singleObject." + h;
eval(bname)._alpha = 0;
fadeInterval = setInterval(fadeIn, 1, bname);
}
I tried with clearInterval(fadeInterval), but this doesn't always work, tried with my_mc.stop() but this doesn't work either.
I tried also to set a variable count that execute the fading olny 5 times, and this work unless I change the item in the drowpdown list before the function complete.
Any ideas?? Hope it was clear!
Thanks
If anyone cares i solved with the Tween class!! All those functions were replaced by one line of code:
function fadeTo(clipName, fadeValue){
new mx.transitions.Tween(eval(clipName), "_alpha", mx.transitions.easing.Regular.easeOut, eval(clipName)._alpha, fadeValue, 1, true);
}

actionscript 3, action after multiple tweening done

I am a newbie in game developing, now i am working with a shooter game for learning purpose.
I have a question,
in my game I created three tween animation :
var myTween:Tween = new Tween(this, "scaleX", Back.easeIn, 1.2, 0, 10);
var myTween2:Tween = new Tween(this, "scaleY", Back.easeIn, 1.2, 0, 10);
var myTween3:Tween = new Tween(this, "alpha", None.easeIn, 1, 0, 10);
This tweens will occur after enemy's health become zero,
What I intended is after the animation, the clip will be removed from stage.
my question is, is there a way to know that all these tweens has finished? I tried to apply TweenEvent.MOTION_FINISH event for each tween , but If i do that i have to create three listenesr ( which will be problematic if I want to create ten tweens).
Thank you
Since all the tweens run for the same duration could you not just add your listener to the last tween and when the handler executes you will know that they have all completed?
Alternatively you could do something like this:
import fl.transitions.Tween;
import fl.transitions.TweenEvent;
import fl.motion.easing.Back;
import fl.transitions.easing.None;
// Populate an array with the tweens
var tweens:Array = [];
tweens.push(new Tween(this, "scaleX", Back.easeIn, 1.2, 0, 10));
tweens.push(new Tween(this, "scaleY", Back.easeIn, 1.2, 0, 10));
tweens.push(new Tween(this, "alpha", None.easeIn, 1, 0, 10));
// Finished tweens count
var finishedCount:int = 0;
// Loop through all the tweens and add a handler for the motion finished event
for (var i:int = 0; i < tweens.length; i ++)
{
// Each of the tweens motion finished event can be assigned to the same handler
Tween(tweens[i]).addEventListener(TweenEvent.MOTION_FINISH, motionFinishedHandler);
}
function motionFinishedHandler(e:TweenEvent):void
{
// Good practice to remove the event listener when it is no longer needed
e.target.removeEventListener(TweenEvent.MOTION_FINISH, motionFinishedHandler);
// Increment the count and test whether it equals the number of tweens
if (++ finishedCount == tweens.length)
trace("Finished");
}
You might also want to consider Greensock's TweenLite which is pretty much the standard for animating objects in Flash and which will allow you to tween multiple properties of the same object in a single call.
+1 for Greensock's TweenLite & TimelineLite.
Makes tweening everything cleaner and easier.

Changing Shape on Button Press Action Script 3.0

Alright, sorry if this is a pretty easy question to get answered, but I looked around through pages and pages on google and couldn't find anything somewhat related. I got a lot of help, but I still can't seem to get this part of my ActionScript working. I have a program, that when running, allows me to paint random color squares on mouse click. I added a button, that is supposed to be able to change the shape being painted from rectangle to circle. I can't seem to get that button to work. This is what my code looks like so far.
var color:Number;
stage.addEventListener(MouseEvent.MOUSE_DOWN, startDrawing);
stage.addEventListener(MouseEvent.MOUSE_UP, stopDrawing);
function startDrawing(e:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, makeShapes);
color = Math.random() * 0xFFFFFF;
}
function stopDrawing(e:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, makeShapes);
}
function makeShapes(e:MouseEvent):void {
var rect:Rect = new Rect(10,10,color);
addChild(rect);
rect.x = mouseX;
rect.y = mouseY;
}
shape_btn.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
function mouseDownHandler(event:MouseEvent):void {
}
At the bottom I left it blank, it seems to be the part I'm stuck on. I've tried simply setting the VAR to my "Ellipse" class I had made, which gets it to work, but only that one time when I click the button. It doesn't stay a circle and allow me to paint with the shape. Again I'm sorry, I felt like I was getting pretty close to the solution, and then I hit a wall.
Hard to understand what the difficulty is, but I'll try to address what I understand.
First, the stage mouse down event will capture your button event, so you might as well get rid of it and stick to one mouse event.
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
private function onMouseDown(ev:Event):void
{
if (ev.target==shape_btn)
changeShape();
else if (ev.target==stage)
startDrawing();
}
Or something along those lines.
Second, I don't know what Rect is. Is it a class you have an access to? This is how I'd do it without a special class:
private function makeShape():void
{
switch (shapeType)
{
case "rect":
drawRect();
break;
case "circle":
drawCircle();
break;
}
}
private function drawRect():void
{
var rect:Shape = new Shape();
rect.graphics.beginFill(color);
rect.graphics.drawRect(0, 0, 10, 10);
rect.x = mouseX;
rect.y = mouseY;
addChild(rect);
}
private function drawCircle():void
{
var circle:Shape = new Shape();
circle.graphics.beginFill(0xff0000);
circle.graphics.drawCircle(0, 0, 10);
circle.x = mouseX;
circle.y = mouseY;
addChild(circle);
}
and finally, the changeShape function:
private function changeShape():void
{
shapeType = shapeType=="rect"?"circle":"rect";
}
There are better ways to go about it, but when dealing with two shape types only this is acceptable.
Of course you need to have a var shapeType:String = "rect" somewhere in your code, outside the functions.
I also think the color randomization should be in the mouse move handler rather than the mouse click. Is that on purpose?

Resources