It looks like you're new here. If you want to get involved, click one of these buttons!
Hi there
I'm having issues with tweens on objects set up by a class for animating text. For some reasons all the tutorials I find assume that the tweens are defined by a function, rather than a class.
In Main when I want to print to screen I do this:
table.insert(writing, Write ("game over"))
tween (2, writing[#writing], {y=50}, tween.easing.backOut, writing[#writing]:erase())
in the Write class:
function Write:init(str)
self.x=WIDTH/2
self.y=HEIGHT/2
self.str=str
self.col=color(0, 244, 255, 255)
--etc
end
function Write:draw()
--draws the text
end
function Write:erase()
print "callback" --ie this is just for debugging.
end
A few questions:
1. Is it possible to put the tween command into the Write:init function, something like this:
tween(2, self, {y=50})
every time I try this, or variations on it, I get an error. I can't define the tween inside the class, is that correct?
tween (2, writing[#writing], {y=50, col.a=0}, tween.easing.backOut, writing[#writing]:erase())
produces an error, as does adding any kind of colour variable
It would be great if someone could tell me where I'm going wrong. Perhaps there's a reason why none of the tweening tutorials I've seen are animating objects defined by a class, and I need to convert the Write class into a set of functions in the Main tab. I'm new to Codea/Lua (my last programming experience was Blitz Basic on the Amiga a long tme ago), and don't have a sense yet of when class or a function is the best way of defining the objects in the game.
thanks for reading
Comments
@yojimbo2000 - I suspect your problem is because you can't use self as the item to be tweened, because self is not a normal table. If you define a table inside your class and tween that, you should be OK.
I'm not sure about the colour variable but that's easy to get around by just tweening an ordinary number and then setting the colour value to that.
I don't understand the last question about the table remove, can you provide more detail?
@Ignatz, thanks for the speedy reply.
OK, I'll investigate putting a table inside the class.
I tried your suggestion of tweening a separate alpha variable, it worked, thanks for that.
the callback is meant to erase the writing object after the tween has finished.
Before I thought about using callback, I had a self.timer variable set in Write:init(), which would manually count down inside the draw loop. When it reached 0 I'd erase that entry. THis is the code from the main draw loop:
Rather than this manual method, I thought it would be more elegant to use the callback to kill the writing when the tween finished. However, I don't know how I would get the correct position in table.remove for this, and also the erase() function seems to get called at the very start of the tween, not the end of the motion
You can't kill a class instance from within itself
Better is to have a table where you keep objects to be deleted, and then empty it in draw, eg something like
Great idea, I'll try that.
Any thoughts on why the callback function is being called at the very start of the tween though?
Try this. Tap screen to start the tween.
There's no real need for a class for this. When I did it, I put messages into a table, and as each one expired, I removed it and tweened the next. That's simpler to manage.
I changed my code above so the erase function gets called when the tween is done.
@dave1707 thanks for that example, the tween motion works great. However, I see the "callback" debug text being printed straightaway, not at the end of the motion. Are you seeing that behaviour too?
UPDATED: Great, it works now! So the callback cannot be inside the class? That's interesting. I think @Ignatz is right, maybe it is better in a table/ function
@yojimbo2000 Here's a version with the callback in the class. I have it clear the string when the tween is done.
@yojimbo2000 I modified the code above to allow different times for different Tweens. again, just tap the screen to start the Tweens.
Ok, combining @dave1707 and @Ignatz's suggestions. I've also placed the tween in the unit function and commented out the touch function. The tween animates, the callback happens when it should, but, setting the value to nil does not kill the object. I've found this previously with objects stored in tables, I had to use remove.table to kill the object. Any ideas how to fix this?
Oh I've just realised, erase is no longer inside the class, so there's no self variable here
@yojimbo2000 See my version above that has the callback in the class.
But when the callback was in the class, it didn't work properly. It was called as soon as the tween started.
@yojimbo2000 You're not looking at my last example where it's in the class and it's correct.
@dave1707
You're right, sorry I didn't see the second version that you posted.
And it works!
I had no idea that you could define a null function in a single line like that with just spaces inbetween each command. That is fantastically useful. Is there a tutorial anywhere explaining this syntax?
Thank you
I guess though that I should have the object stored in a table inside the class though, because the self.str="" isn't killing the object as such, just making it invisible, right? ie, as the game went on, the pairs(writing) loop would continue to get longer (I'll be using this every time the player scores points). Although maybe the processor overhead will be minimal if things aren't actually being drawn.
@yojimbo2000 I was just clearing the str variable because I didn't know what you really want to do. You can add as much code as you want in that function to do whatever.
@yojimbo2000 Here's a version where I increased the table count and then I delete the table entry when the tween is done.
Ok, I figured it out. I made the callback function set the string to nil, and this is what I changed the main draw command to:
So the actual act of deletion occurs outside the class in the main loop.
Thanks to both of you for your help, I've learned a lot!
Sorry I was typing my comment at the same time as yours, I think our solutions are the same, aren't they?
@yojimbo2000 - IMHO, a simple table will be better than a class - and efficient - because it works like a queue, which is exactly what you want. Using a class and setting messages to blanks without killing them when they finish is pretty ugly.
I've written about anonymous functions here
https://coolcodea.wordpress.com/2013/09/13/111-anonymous-functions-and-the-mysterious-_g/
@yojimbo2000 They both work the same. I like using seperate self variables for different conditions.
@Ignatz you could be right there. I might have a go at recoding it as a table/ function in the main tab just to see what the difference is. But if you look at mine and @dave1707 's most recent code, we do have an efficient way of properly killing the instance in the draw loop, after it has been set to nil.
@yojimbo2000 - below is a table of functions I use in my 3D dungeon, to show messages in sequence, one after the other, for variable times.
to add a message:
Messages.Add{text=a.text,length=a.length}
(note I send a table of named parameters through, enabling me to define only the message elements I need)
to show messages in draw:
Messages.draw()
(which also manages deletion)
I have a variation on this in my 2D sidescroller, which shows multiple messages. scrolling them upwards and fading them, so they can stack on each other.
@Ignatz oh, you're the coolcodea author! I love that blog! It's an incredible resource
Below is my scrolling message function set, you can see the result here
https://coolcodea.wordpress.com/2014/09/16/162-2d-platform-game-6-extras/
Message.draw
needs to be given a player object in this case so it can figure out where to position the message (just above the player's head)@Ignatz I saw a version of the code for the 2d platform game, I was the guy that asked that question today on that page of the blog. It looks very different from a class, when you store it in a table like that. I still don't understand the first 2 lines though. Is the messages.text {} table nested inside (like a dimension) the messages {} table? Are the functions also part of the messages {} table? You have to forgive me, object oriented stuff (if that's the correct word for classes and functions) is still new to me, and I don't entirely get the .suffix convention
See if this helps
https://coolcodea.wordpress.com/2013/04/12/31-a-lesson-from-the-roller-coaster-demo/
https://coolcodea.wordpress.com/2014/10/01/169-why-tables-and-classes-are-so-useful/
https://coolcodea.wordpress.com/2013/06/14/84-a-practical-example-showing-the-value-of-classes/
Essentially, you can put functions in a table, which just means you add a prefix with the table name. The same goes for variables. This enables you to keep this stuff completely separate from your other code and not risk duplicating variable names etc.
Just replying to something @Ignatz said waaay above: you can use self as the table for a tween.
@JakAttak but you can't just make "self" by itself the object of the tween though can you, like I was trying to do right at the top of the thread? It has to be self.something, whether that something is a position vector, like @Dave1707 came up with here, or a table inside the table, like something={str=srt, x=xs, y=ys, col=col} etc.
Or have you managed it with just self as the object?
tween(1, self, { x = 100 }, tween.easing.linear)
works perfectly fineHere's a small example:
@JakAttak you're right. I went back to have another look at my original code. Apart from not using the callback function correctly, the destination was a variable I'd defined locally somewhere else (it was from my first Codea project, and the code was a mess basically). I thought I'd tried every combo (obviously not). I fixed all of that, and it now works. i.e. I have a class for animating text on the screen, the tween in the init of the class with self as object, and a callback that tells the draw loop to kill the text once the animation is done. Thank you everyone, I learnt a lot.
Slightly off-topic follow-on question, my FPS drops from 60 to 30 or lower when there's lots of action on screen (ie explosions plus this text routine displaying points, multipliers, and so on). Is it faster to create an image of the text, and then draw it as a mesh, than using text() for the frame-by-frame drawing? Or is it the tweens that are the processor drag? I also have the same question for my explosion sparks, which are currently drawn with ellipse(). Would an image mesh be faster? Generally, I'm very impressed with the speed of codea. I have 50 physical bodies bouncing around, all drawn with meshes, and FPS is a consistent 60 on the iPad Air.
Tweens are just number interpolations, and very fast. Ellipses (in great numbers) can be slow, I recall from previous threads.
But why don't you try it different ways and see what happens? That is the best way to discover the fastest approach.