本章打包的素材:http://files.cnblogs.com/mignet/animate.7z
在這一章我們將通過動畫來豐富我們的游戲效果。
使用action來操作actor:
很多同學應該都很清楚action是什么東東,所以這里僅僅舉個最常見的例子。
moveTo (x, y);
moveTo (x, y, duration);
moveTo (x, y, duration, interpolation);
rotateTo (rotation);
rotateTo (rotation, duration);
rotateTo (rotation, duration, interpolation);
來個連續動作:
Actor actor = new Actor(); float x = 100.0f, y = 100.0f, rotation = 0.0f, duration = 1.0f; actor.addAction(sequence( moveTo(x, y), rotateTo(rotation), delay(duration), parallel(moveBy(50.0f, 0.0f, 5.0f), rotateBy(90.0f, 5.0f, Interpolation.swingOut))));
插值算法(Interpolation)和前文提到的一樣。現在,發現actor+action的威力了嗎,哈哈
Libgdx支持的action列表:
•add(): This adds an action to an actor.
•alpha(): This sets the alpha value of an actor's color.
•color(): This sets the color of an actor.
•fadeIn() and fadeOut(): These are convenience actions to set the alpha value of an actor's color to 1 or 0, respectively.
•hide(): This sets the visibility of an actor to false.
•layout(): This sets the actor's layout to enabled or disabled.
•moveBy() and moveTo(): These move an actor by a relative amount or to a specific location.
•removeActor(): This removes the actor to which this action is attached. Alternatively, another actor that is to be removed can be specified.
•rotateBy() and rotateTo(): These rotate an actor by a relative amount or to a specific angle of rotation.
•run(): This runs a Runnable (the code will be executed in a separate thread).
•scaleBy() and scaleTo(): These scale an actor by a relative amount or to a specific scale.
• show(): This sets the visibility of an actor to true.
• sizeBy() and sizeTo(): These resize an actor by a relative amount or to a specific size.
• touchable(): This sets the touchability of an actor(refer to Touchable enumerator).
• visible(): This sets the visibility of an actor.
控制執行的順序和時間:
• after(): This waits for other actions of an actor to finish before its action is executed. Note that this action will only wait for other actions that were already added to an actor prior to this.
• delay(): This delays the execution of an action.
• forever(): This repeats an action forever.
• parallel():This executes a list of actions at the same time.
• repeat(): This repeats an action for a given number of times.
• sequence(): This executes a list of actions one after another.
讓游戲菜單動起來:
動畫效果:首先金幣和兔子頭是看不見的,接着金幣淡入和比例從0%提高到100%,這樣看起來好像是跳出了水面到中心屏幕上一樣,短暫的停頓之后,兔子頭出現在右上角,兔子頭移動,就好像它是跳躍到它前面的其他的岩石上。
首先,修改MenuScreen:
private Table buildObjectsLayer() { Table layer = new Table(); // + Coins imgCoins = new Image(skinCanyonBunny, "coins"); layer.addActor(imgCoins); imgCoins.setOrigin(imgCoins.getWidth() / 2, imgCoins.getHeight() / 2); imgCoins.addAction(sequence( moveTo(135, -20), scaleTo(0, 0), fadeOut(0), delay(2.5f), parallel(moveBy(0, 100, 0.5f, Interpolation.swingOut), scaleTo(1.0f, 1.0f, 0.25f, Interpolation.linear), alpha(1.0f, 0.5f)))); // + Bunny imgBunny = new Image(skinCanyonBunny, "bunny"); layer.addActor(imgBunny); imgBunny.addAction(sequence( moveTo(655, 510), delay(4.0f), moveBy(-70, -100, 0.5f, Interpolation.fade), moveBy(-100, -50, 0.5f, Interpolation.fade), moveBy(-150, -300, 1.0f, Interpolation.elasticIn))); return layer; }
讓menu和Option動起來:
添加MenuScreen:
private void showMenuButtons(boolean visible) { float moveDuration = 1.0f; Interpolation moveEasing = Interpolation.swing; float delayOptionsButton = 0.25f; float moveX = 300 * (visible ? -1 : 1); float moveY = 0 * (visible ? -1 : 1); final Touchable touchEnabled = visible ? Touchable.enabled : Touchable.disabled; btnMenuPlay.addAction(moveBy(moveX, moveY, moveDuration, moveEasing)); btnMenuOptions.addAction(sequence(delay(delayOptionsButton), moveBy(moveX, moveY, moveDuration, moveEasing))); SequenceAction seq = sequence(); if (visible) seq.addAction(delay(delayOptionsButton + moveDuration)); seq.addAction(run(new Runnable() { public void run() { btnMenuPlay.setTouchable(touchEnabled); btnMenuOptions.setTouchable(touchEnabled); } })); stage.addAction(seq); } private void showOptionsWindow(boolean visible, boolean animated) { float alphaTo = visible ? 0.8f : 0.0f; float duration = animated ? 1.0f : 0.0f; Touchable touchEnabled = visible ? Touchable.enabled : Touchable.disabled; winOptions.addAction(sequence(touchable(touchEnabled), alpha(alphaTo, duration))); }
修改:
private Table buildOptionsWindowLayer () { ... // Make options window slightly transparent winOptions.setColor(1, 1, 1, 0.8f); // Hide options window by default showOptionsWindow(false, false); if (debugEnabled) winOptions.debug(); // Let TableLayout recalculate widget sizes and positions winOptions.pack(); // Move options window to bottom right corner winOptions.setPosition(Constants.VIEWPORT_GUI_WIDTH - winOptions.getWidth() - 50, 50); return winOptions; } private void onOptionsClicked () { loadSettings(); showMenuButtons(false); showOptionsWindow(true, true); } private void onCancelClicked () { showMenuButtons(true); showOptionsWindow(false, true); AudioManager.instance.onSettingsUpdated(); }
使用的圖像序列動畫:
用texture packer把序列圖片打包。
Libgdx播放動畫的不同模式:
• NORMAL: This plays the animation once (first frame to last)
• REVERSED: This plays the animation once (last frame to first)
• LOOP: This plays the animation in a loop (first frame to last)
• LOOP_REVERSED: This plays the animation in a loop (last frame to first)
• LOOP_PINGPONG: This plays the animation in a loop (first frame, to last,to first)
• LOOP_RANDOM: This plays the animation in a loop (random frames)
給游戲場景增加動畫:
這三個動畫將分別使用不同的模式:
1. The first animation animCopterTransform plays all frames once (play mode:NORMAL; frame progression: 01, 02, 03, 04, 05).
2. The second animation animCopterRotate plays the last two frames in a ping-pong loop(play mode: LOOP_PINGPONG; frame progression: 04, 05, 05,04, [restart at first frame]).
3. Lastly, the third animation animCopterTransformBack is simply the reverse of the first animation (play mode: REVERSED; frame progression: 05, 04, 03,02, 01).
首先,在Assets添加素材:
public class AssetGoldCoin { public final AtlasRegion goldCoin; public final Animation animGoldCoin; public AssetGoldCoin(TextureAtlas atlas) { goldCoin = atlas.findRegion("item_gold_coin"); // Animation: Gold Coin Array<AtlasRegion> regions = atlas.findRegions("anim_gold_coin"); AtlasRegion region = regions.first(); for (int i = 0; i < 10; i++) regions.insert(0, region); animGoldCoin = new Animation(1.0f / 20.0f, regions, Animation.LOOP_PINGPONG); } } public class AssetBunny { public final AtlasRegion head; public final Animation animNormal; public final Animation animCopterTransform; public final Animation animCopterTransformBack; public final Animation animCopterRotate; public AssetBunny(TextureAtlas atlas) { head = atlas.findRegion("bunny_head"); Array<AtlasRegion> regions = null; AtlasRegion region = null; // Animation: Bunny Normal regions = atlas.findRegions("anim_bunny_normal"); animNormal = new Animation(1.0f / 10.0f, regions, Animation.LOOP_PINGPONG); // Animation: Bunny Copter - knot ears regions = atlas.findRegions("anim_bunny_copter"); animCopterTransform = new Animation(1.0f / 10.0f, regions); // Animation: Bunny Copter - unknot ears regions = atlas.findRegions("anim_bunny_copter"); animCopterTransformBack = new Animation(1.0f / 10.0f, regions, Animation.REVERSED); // Animation: Bunny Copter - rotate ears regions = new Array<AtlasRegion>(); regions.add(atlas.findRegion("anim_bunny_copter", 4)); regions.add(atlas.findRegion("anim_bunny_copter", 5)); animCopterRotate = new Animation(1.0f / 15.0f, regions); } }
修改AbstractGameObject:
public float stateTime; public Animation animation; public void setAnimation(Animation animation) { this.animation = animation; stateTime = 0; } public void update (float deltaTime) { stateTime += deltaTime; if (body == null) { updateMotionX(deltaTime); updateMotionY(deltaTime); // Move to new position position.x += velocity.x * deltaTime; position.y += velocity.y * deltaTime; } else { position.set(body.getPosition()); rotation = body.getAngle() * MathUtils.radiansToDegrees; } }
修改GoldCoin:
private void init() { dimension.set(0.5f, 0.5f); setAnimation(Assets.instance.goldCoin.animGoldCoin); stateTime = MathUtils.random(0.0f, 1.0f); // Set bounding box for collision detection bounds.set(0, 0, dimension.x, dimension.y); collected = false; } public void render(SpriteBatch batch) { if (collected) return; TextureRegion reg = null; reg = animation.getKeyFrame(stateTime, true); batch.draw(reg.getTexture(), position.x, position.y, origin.x, origin.y, dimension.x, dimension.y, scale.x, scale.y, rotation, reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(), reg.getRegionHeight(), false, false); }
現在,給兔子頭加動畫,主要是螺旋槳飛行:
這里用到了狀態機的概念,我們兔子頭就是有限狀態機,也就是說,它在任何時候一定處於有限狀態的某種狀態中,並且在合適的時候進行狀態的切換。
我們來修改BunnyHead:
private Animation animNormal; private Animation animCopterTransform; private Animation animCopterTransformBack; private Animation animCopterRotate; public void init () { dimension.set(1, 1); animNormal = Assets.instance.bunny.animNormal; animCopterTransform = Assets.instance.bunny.animCopterTransform; animCopterTransformBack = Assets.instance.bunny.animCopterTransformBack; animCopterRotate = Assets.instance.bunny.animCopterRotate; setAnimation(animNormal); // Center image on game object origin.set(dimension.x / 2, dimension.y / 2); ... } @Override public void update(float deltaTime) { super.update(deltaTime); if (velocity.x != 0) { viewDirection = velocity.x < 0 ? VIEW_DIRECTION.LEFT : VIEW_DIRECTION.RIGHT; } if (timeLeftFeatherPowerup > 0) { if (animation == animCopterTransformBack) { // Restart "Transform" animation if another feather power-up // was picked up during "TransformBack" animation. Otherwise, // the "TransformBack" animation would be stuck while the // power-up is still active. setAnimation(animCopterTransform); } timeLeftFeatherPowerup -= deltaTime; if (timeLeftFeatherPowerup < 0) { // disable power-up timeLeftFeatherPowerup = 0; setFeatherPowerup(false); setAnimation(animCopterTransformBack); } } dustParticles.update(deltaTime); // Change animation state according to feather power-up if (hasFeatherPowerup) { if (animation == animNormal) { setAnimation(animCopterTransform); } else if (animation == animCopterTransform) { if (animation.isAnimationFinished(stateTime)) setAnimation(animCopterRotate); } } else { if (animation == animCopterRotate) { if (animation.isAnimationFinished(stateTime)) setAnimation(animCopterTransformBack); } else if (animation == animCopterTransformBack) { if (animation.isAnimationFinished(stateTime)) setAnimation(animNormal); } } } @Override public void render(SpriteBatch batch) { TextureRegion reg = null; // Draw Particles dustParticles.draw(batch); // Apply Skin Color batch.setColor(CharacterSkin.values()[GamePreferences.instance.charSkin] .getColor()); float dimCorrectionX = 0; float dimCorrectionY = 0; if (animation != animNormal) { dimCorrectionX = 0.05f; dimCorrectionY = 0.2f; } // Draw image reg = animation.getKeyFrame(stateTime, true); batch.draw(reg.getTexture(), position.x, position.y, origin.x, origin.y, dimension.x + dimCorrectionX, dimension.y + dimCorrectionY, scale.x, scale.y, rotation, reg.getRegionX(), reg.getRegionY(), reg.getRegionWidth(), reg.getRegionHeight(), viewDirection == VIEW_DIRECTION.LEFT, false); // Reset color to white batch.setColor(1, 1, 1, 1); }
呼,終於忙完了。
至此12章全文結束,所有章節的源碼都在qq群文件中,大家知悉。