Pages

Saturday, August 17, 2013

Notes on Lightweight SWF (LWF) framework for Unity

Lightweight SWF, or LWF, is an open-source framework created by Gree for importing Flash animations into Unity. It's official page: https://github.com/gree/lwf

The infos provided in their wiki are a bit messy, thus this post will try to serve as another organised starting point for using LWF.

For starters, you need to learn how to convert SWF into something readable by the LWF plugins, using a software provided by them called LWFS:


And here's a tutorial (provided by Gree) on how to play the (converted) SWF inside Unity:
https://github.com/gree/lwf/wiki/ForUnity

Extra infos about the Flash animation production guidelines are also provided by Gree:
http://gree.github.io/lwf-demo/pdf/FLASHforLWFproductionguideline.pdf

The guidelines are a bit confusing for me, so I'm just gonna write my own version below.

(New) LWF Guidelines
(Once you get a grasp on how the texture and data loading works, and how to display the SWF into the Unity scene...)

1. You can have as many MovieClips in your Flash file as you want. Despite that, I would rather prefer to categorised different animations to different Flash files, before exporting them.
For instance, a character can have an Idle, Walk and Attack animation. If you are exporting them through different Flash files, it'll probably be less messy to put all 3 of them inside a single Flash file.

2. Each of the MovieClips MUST have a Linkage name. In case you don't know how to set up a the Linkage name for a MovieClip:
- Select the "Library" tab in Flash, right click on your MovieClip and select "Properties".
- A small window will popup. Under the "ActionScript Linkage", make sure you check both "Export for ActionScript" and "Export in frame 1".
- Inside the "Class" textfield, place in any Linkage name you like, preferably the one similar to your MovieClip's name as it's easier to keep track of.
- Once you're done, click "OK" and you should be able to see your Linkage name under Library's "AS Linkage".


3. Scene need to have at least ONE MovieClip, with an instance name. It can be any MovieClip you have in your Library. If your scene is empty, drag and place ANY MovieClip in the first frame, give it an instance name (say "animation") in the "Properties" tab.
It doesn't matter what instance name you put, as long as there's at least one MovieClip in the scene which has an instance name.
(Further Note: Since the instance name don't really affect anything else in the data, I just take the liberty and name all the first frame MovieClip's instance name as "animation" for easy referral sake.)

EDITED: I've previously stated that the Flash's scene Properties' Size should be set to your game'e resolution in Unity, in order for it to be displayed correctly. That is no longer necessary as I found another way to go around, by using the width and height properties I acquired from LWF itself.

I've since updated the script, you can check out the changes yourself, it's under the Start() method's ScaleForWidth() and ScaleForHeight().

Attaching and Detaching Movies, Callback Methods, etc
(Once you had made sure your Flash files meet the 4 requirements I stated above, export it, convert into LWF data using LWFS, and you can move on to the next step)

Not sure if the attaching and detaching of Movies is mentioned anywhere in the wiki, but after reading an Issue posted by some LWF users on its GitHub site, I've come to learn about these very useful methods for playing and controlling the Flash animations using the framework.

To start with, this is the full LWF Animation manager I wrote to handle the SWF animations:
LWFAnimator (Download Link)

What each lines do:

Public Variables
1. instanceName
- The name of the MovieClip that was mentioned in (New) LWF Guidelines section, where you need to add at least ONE MovieClip, with an instance name, that's the instanceName I'm talking about, which for my case, I usually put "animation". I would advice you to do the same.
2. animName
- For storing the .bytes data (aka your SWF animation's) name (e.g. "character_anim"; no need to put ".bytes")
3. folderPath
-  For placing the path of the folder (in Resources folder) where you placed your LWF animation's texture and .bytes data in (e.g. "LWF/character/"; a "/" is required at the end of the string)
4. clips
- For storing the information of the MovieClip, such as its name, linkage, and wrapMode

Serializable Class
1. FlashMovieClip
- for storing a MovieClip's info:
a. clipName
b. linkage
c. wrapMode (which was simply written by me, to simulate a similar kind of animation behaviour in Unity)

Start() method
1. SetLoader();
- The basic required method for loading the LWF data
2. Load(folderPath + animName, folderPath);
- The basic required method for loading the LWF data
3. ScaleForWidth(Screen.width / 2);
- Scale the (to-be-spawned) sprite to a size suitable for the screen (width)
- Remember in the (New) LWF Guidelines section, I mentioned about setting the scene's Properties > Size to your game's resolution, this is where it matters.
- As for the divide by 2 thing, I also have no idea why, it just works that way. But I would still prefer to retain the "divide by 2 part" while setting the Flash scene's size to the game's resolution, for formality sake.
4. ScaleForHeight(Screen.height / 2);
- Scale the (to-be-spawned) sprite to a size suitable for the screen (height)
- The rest is the same as ScaleForWidth
5. StopMovie(instanceName);
- Remember in the (New) LWF Guidelines section, I mentioned that you need at least ONE MovieClip, with an instance name in the scene?
- Basically, once you click "Play" in Unity's editor, the MovieClip's animation will start playing and looping non stop. Which is why I'm putting a stop to the animation from the start, by its instance name, since we're not gonna use it anyway (I'll explain why later)
6. SetVisibleMovie(instanceName, false);
- As we're not gonna use the MovieClip's animation, might as well set it invisible, in case if you have a visible frame in the first frame of the MovieClip

PlayClip(string _clipName) method
1. First, it'll check whether if there's any Movie (or MovieClip) by supplied "_clipName" attached to the LWFAnimator object. If there's a Movie by the name of "_clipName" to be found, it'll first detach the Movie, before attaching a new one next.
2. GetClip(_clipName)
- Returns a variable of type:FlashMovieClip (Serializable), which the method can use to attach a MovieClip on the LWFAnimator
3. lwf.rootMovie.AttachMovie(linkageName, clipName, enterFrameCallbackMethod);
- For attaching a MovieClip of the clipName and linkageName, with an enterFrame callback method called "EnterFrameCallback"
4. the attached Movie was store in an array which we'll use later

PlayClip(string _clipName, bool _stopAllPrevMovies) method
1. Same as the one before, except you can decide whether to stop all previous attached movies. So that, you know, only one animation will be played per session. For instance, you don't want a character's Idle and Walk animation to play at the same time right? That's why you use this method.

DetachAllMovies() method
1. Detach all the movies ever added to the LWFAnimator.

EnterFrameCallback(LWF.Movie _movie) method
1, Check if a particular animation reaches its final frame. You can access various information about the movie to simulate whatever behaviour you want too, but for now, I just want to simulate the WrapModes.
2. For clip with Default and Clamp (or Once) wrapMode, I would want the animation to be detached from the LWFAnimator, so that it would stop playing. I was originally planning to just Stop() and SetVisible(false) using the methods provided, but it doesn't seems to be replayable when you use its Play() nor GotoAndPlay() method later on.
3. For clip with ClampForever wrapMode, I want the animation to stop at the last frame, so I uses the Stop() method provided.

Conclusion
That's about all that I have for LWF for now, if I have more I'll make a part 2 of the post.
Hope the LWFAnimator can help my future self or some of you in getting started on using the framework.
Overall it's a pretty good (free) framework to use.

11 comments:

Anonymous said...

I have some problem with convert swf file It said "undefined method `width'"

I have one movieclip in scene
already set linkage and publish setting to flash7 and actionscript1

what do I miss here?

Thank you

Lee Zhi Fei (JakeL168) said...

hmm... does that error came with the codes I provided?
Btw, my usual workaround is that I don't include a single actionscipt inside the SWF.
If you're still getting all the error, do you mind sending me your SWF file so I can have a test at it?

Interesting video games said...

It's good that you can have as many movie clips as you want. I understand your point about naming. Maybe that's something they can add as an improvement in a little while. Perhaps they didn't think of it.

I also understand the need to have a movie clip in each scene. That makes the project more interesting.

Goran Belovari said...

Hi,

I have a bit of a problem with scaling and quality degradation when using:

ScaleForWidth
ScaleForHeight

the smaller screen size, the worse movie quality gets and I really can't figure it why.
Maybe it's good to mention that same happens when using classic Unity scale controls (transform.localScale in script).

Any advice would be much appreciated!

Lee Zhi Fei (JakeL168) said...

@Goran Belovari: Regarding that Scale thing, I actually have a fix, but I totally forgot to update the code. Will try to do it tonight~ :)

Goran Belovari said...

Thanks jakel168, I think that would help many others too :)

thank you very much for time you took to write this awesome tutorial for us!

the only way I'm dealing with that is to boost Aniso level in Texture settings, but that comes with a great cost of performance...

Lee Zhi Fei (JakeL168) said...

@Goran Belovari
Actually you know what, I realise your problem might have something to do with the issue I posted on Gree's Git forum, whereby the smaller my LWF animation/screen size is, the worst the quality gets:
https://github.com/gree/lwf/issues/21

Though the only solution I ended up with was simply just increasing the resolution of my images.

P.S> Never mess around with your transform.localScale if it isn't necessary. Always keep it at Vector3.one.

Goran Belovari said...

@jakel168

thanks for the advice, I'll try to make even bigger resolution files...

I get what you mean for localScale, I think it affects in general to that GameObject.I also posted issue in Gree's GitHub:
https://github.com/gree/lwf/issues/35

where I tryed to explain what kind of problem I get when using LWFObject.Movie.Scale function - it messes up everything :)

so till I figure how to use that Scale I have 2 options:
- localScale
- ScaleFor*

Anyway thanks for all the help you provided!

Lee Zhi Fei (JakeL168) said...

@Goran Belovari
Welcome.
Anyway I've updated the LWFAnimator which fix certain Scaling issue.

Goran Belovari said...

Hi jakel168's,

Thanks for the update...

good people at GitHub answered on my question with this proposal:
https://github.com/gree/lwf/issues/21#issuecomment-25378609

I tried using the advanced texture type and I am getting much better results - the only thing I changed is Filter point to bilinear, don't like the grainy look especially on text :)

So I hope this will help to anyone else as it did to me.

Lee Zhi Fei (JakeL168) said...

@Goran Belovari
For my case, I wanted it to retain the pixel look I originally intended. (In the issue I posted in LWF) I drew the star to be 8px x 8px in a 1024px x 768px game's resolution, and I don't really intend to enlarge it in anyway to be honest.

At the moment, since LWF doesn't render the image well to the effect I want, I just render the entire animation (frame by frame) and animate them using 2d toolkit, while retaining the Point filter.