[libgdx游戏开发教程]使用Libgdx进行游戏开发(8)-没有美工的程序员,能够依赖的还有粒子系统

来源:互联网 发布:ubuntu修改系统语言 编辑:程序博客网 时间:2024/06/05 18:05
添加特效,善用粒子系统。

这一章我们将使用Libgdx的粒子系统线性插值以及其他的方法来增加一些特效。

你也可以使用自己编辑的粒子效果,比如这个粒子文件dust:http://files.cnblogs.com/mignet/particles.zip

这个灰尘的特效用在兔子头在地面跑的时候,啪啪的一溜烟。

线性插值可以让我们的摄像机在移动的时候更平滑。

当然,之前提到的背景上的山要实现视差移动效果也要实现。

白云会用随机的速度从右向左飘。

GUI的部分也要增加些效果比如掉了命,得了分等。

粒子系统通常用来模拟复杂的特效:比如fire, smoke, explosions等等.

ParticleEffect简介:

• start(): This starts the animation of the particle effect
• reset(): This resets and restarts the animation of the particle effect
• update(): This must be called to let the particle effect act in accordance to time
• draw(): This renders the particle effect at its current position
• allowCompletion(): This allows emitters to stop smoothly even if particle effects are set to play continuously
• setDuration(): This sets the overall duration the particle effect will run
• setPosition(): This sets the position to where it will be drawn
• setFlip(): This sets horizontal and vertical flip modes
• save(): This saves a particle effect with all its settings to a file
• load(): This loads a particle effect with all its settings from a saved file
• dispose(): This frees all resources allocated by the particle effect

粒子效果通常需要一个粒子发射器ParticleEmitter:

ParticleEffect effect = new ParticleEffect();ParticleEmitter emitter = new ParticleEmitter();effect.getEmitters().add(emitter);emitter.setAdditive(true);emitter.getDelay().setActive(true);emitter.getDelay().setLow(0.5f);// ... more code for emitter initialization ...

当然,不建议在代码里初始化例子发射器。因为发射器有20多个属性,要是在代码里初始化会很杂乱并且不容易维护。我们使用Libgdx的编辑器来编辑想要的粒子。

https://github.com/libgdx/libgdx/wiki/Particle-editor

我们来调个灰尘特效吧:

保存文件到CanyonBunny-android/assets/particles/dust.pfx

虽然并没有规定粒子文件要用什么文件后缀,但是我们统一叫pfx。记得把图片https://github.com/libgdx/libgdx/blob/master/extensions/gdx-tools/assets/particle.png

也保存到相同的文件夹。

首先在兔子头BunnyHead里添加代码:

public ParticleEffect dustParticles = new ParticleEffect();public void init () {...// Power-upshasFeatherPowerup = false;timeLeftFeatherPowerup = 0;// ParticlesdustParticles.load(Gdx.files.internal("particles/dust.pfx"),Gdx.files.internal("particles"));}@Overridepublic void update (float deltaTime) {    super.update(deltaTime);    ...    dustParticles.update(deltaTime);}@Overridepublic void render (SpriteBatch batch) {    TextureRegion reg = null;    // Draw Particles    dustParticles.draw(batch);    // Apply Skin Color    ...}

 让灰尘跟着兔子:

 
   protected void updateMotionY(float deltaTime) {        switch (jumpState) {        case GROUNDED:            jumpState = JUMP_STATE.FALLING;            if (velocity.x != 0) {                dustParticles.setPosition(position.x + dimension.x / 2,                        position.y);                dustParticles.start();            }            break;...    if (jumpState != JUMP_STATE.GROUNDED){            dustParticles.allowCompletion();            super.updateMotionY(deltaTime);        }    }}

ok,跑起..

接下来,让云飘起来:

private Cloud spawnCloud() {        Cloud cloud = new Cloud();        cloud.dimension.set(dimension);        // select random cloud image        cloud.setRegion(regClouds.random());        // position        Vector2 pos = new Vector2();        pos.x = length + 10; // position after end of level        pos.y += 1.75; // base position        // random additional position        pos.y += MathUtils.random(0.0f, 0.2f)                * (MathUtils.randomBoolean() ? 1 : -1);        cloud.position.set(pos);        // speed        Vector2 speed = new Vector2();        speed.x += 0.5f; // base speed        // random additional speed        speed.x += MathUtils.random(0.0f, 0.75f);        cloud.terminalVelocity.set(speed);        speed.x *= -1; // move left        cloud.velocity.set(speed);        return cloud;    }    @Override    public void update(float deltaTime) {        for (int i = clouds.size - 1; i >= 0; i--) {            Cloud cloud = clouds.get(i);            cloud.update(deltaTime);            if (cloud.position.x < -10) {                // cloud moved outside of world.                // destroy and spawn new cloud at end of level.                clouds.removeIndex(i);                clouds.add(spawnCloud());            }        }    }

线性插值,让摄像机平滑移动到跟随的目标(Libgdx已经实现了lerp)CameraHelper

private final float FOLLOW_SPEED = 4.0f;        public void update(float deltaTime) {        if (!hasTarget())            return;        position.lerp(target.position, FOLLOW_SPEED * deltaTime);        // Prevent camera from moving down too far        position.y = Math.max(-1f, position.y);    }

让岩石浮在水面上Rocks:

private final float FLOAT_CYCLE_TIME = 2.0f;    private final float FLOAT_AMPLITUDE = 0.25f;    private float floatCycleTimeLeft;    private boolean floatingDownwards;    private Vector2 floatTargetPosition;    private void init() {        dimension.set(1, 1.5f);        regEdge = Assets.instance.rock.edge;        regMiddle = Assets.instance.rock.middle;        // Start length of this rock        setLength(1);        floatingDownwards = false;        floatCycleTimeLeft = MathUtils.random(0, FLOAT_CYCLE_TIME / 2);        floatTargetPosition = null;    }    @Override    public void update(float deltaTime) {        super.update(deltaTime);        floatCycleTimeLeft -= deltaTime;        if (floatTargetPosition == null)            floatTargetPosition = new Vector2(position);        if (floatCycleTimeLeft <= 0) {            floatCycleTimeLeft = FLOAT_CYCLE_TIME;            floatingDownwards = !floatingDownwards;            floatTargetPosition.y += FLOAT_AMPLITUDE                    * (floatingDownwards ? -1 : 1);        }        position.lerp(floatTargetPosition, deltaTime);    }

让山随着兔子视差Mountains:

public void updateScrollPosition(Vector2 camPosition) {        position.set(camPosition.x, position.y);    }    private void drawMountain(SpriteBatch batch, float offsetX, float offsetY,            float tintColor, float parallaxSpeedX) {        TextureRegion reg = null;        batch.setColor(tintColor, tintColor, tintColor, 1);        float xRel = dimension.x * offsetX;        float yRel = dimension.y * offsetY;        // mountains span the whole level        int mountainLength = 0;        mountainLength += MathUtils.ceil(length / (2 * dimension.x)                * (1 - parallaxSpeedX));        mountainLength += MathUtils.ceil(0.5f + offsetX);        for (int i = 0; i < mountainLength; i++) {            // mountain left            reg = regMountainLeft;            batch.draw(reg.getTexture(), origin.x + xRel + position.x                    * parallaxSpeedX, origin.y + yRel + 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);            xRel += dimension.x;            // mountain right            reg = regMountainRight;            batch.draw(reg.getTexture(), origin.x + xRel + position.x                    * parallaxSpeedX, origin.y + yRel + 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);            xRel += dimension.x;        }        // reset color to white        batch.setColor(1, 1, 1, 1);    }    @Override    public void render(SpriteBatch batch) {        // 80% distant mountains (dark gray)        drawMountain(batch, 0.5f, 0.5f, 0.5f, 0.8f);        // 50% distant mountains (gray)        drawMountain(batch, 0.25f, 0.25f, 0.7f, 0.5f);        // 30% distant mountains (light gray)        drawMountain(batch, 0.0f, 0.0f, 0.9f, 0.3f);    }

把这个加到worldcontroller里:

public void update(float deltaTime) {        handleDebugInput(deltaTime);        if (isGameOver()) {            timeLeftGameOverDelay -= deltaTime;            if (timeLeftGameOverDelay < 0)                backToMenu();        } else {            handleInputGame(deltaTime);        }        level.update(deltaTime);        testCollisions();        cameraHelper.update(deltaTime);        if (!isGameOver() && isPlayerInWater()) {            lives--;            if (isGameOver())                timeLeftGameOverDelay = Constants.TIME_DELAY_GAME_OVER;            else                initLevel();        }        level.mountains.updateScrollPosition(cameraHelper.getPosition());    }

现在,增加GUI的特效。

首先是掉了命:

在worldcontroller里加public float livesVisual;

private void init() {        Gdx.input.setInputProcessor(this);        cameraHelper = new CameraHelper();        lives = Constants.LIVES_START;        livesVisual = lives;        timeLeftGameOverDelay = 0;        initLevel();    }    public void update(float deltaTime) {        handleDebugInput(deltaTime);        if (isGameOver()) {            timeLeftGameOverDelay -= deltaTime;            if (timeLeftGameOverDelay < 0)                backToMenu();        } else {            handleInputGame(deltaTime);        }        level.update(deltaTime);        testCollisions();        cameraHelper.update(deltaTime);        if (!isGameOver() && isPlayerInWater()) {            lives--;            if (isGameOver())                timeLeftGameOverDelay = Constants.TIME_DELAY_GAME_OVER;            else                initLevel();        }        level.mountains.updateScrollPosition(cameraHelper.getPosition());        if (livesVisual > lives)            livesVisual = Math.max(lives, livesVisual - 1 * deltaTime);    }

同时,在WorldRenderer里相应的修改:

private void renderGuiExtraLive(SpriteBatch batch) {        float x = cameraGUI.viewportWidth - 50 - Constants.LIVES_START * 50;        float y = -15;        for (int i = 0; i < Constants.LIVES_START; i++) {            if (worldController.lives <= i)                batch.setColor(0.5f, 0.5f, 0.5f, 0.5f);            batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120,                    100, 0.35f, -0.35f, 0);            batch.setColor(1, 1, 1, 1);        }        if (worldController.lives >= 0                && worldController.livesVisual > worldController.lives) {            int i = worldController.lives;            float alphaColor = Math.max(0, worldController.livesVisual                    - worldController.lives - 0.5f);            float alphaScale = 0.35f * (2 + worldController.lives - worldController.livesVisual) * 2;            float alphaRotate = -45 * alphaColor;            batch.setColor(1.0f, 0.7f, 0.7f, alphaColor);            batch.draw(Assets.instance.bunny.head, x + i * 50, y, 50, 50, 120,                    100, alphaScale, -alphaScale, alphaRotate);            batch.setColor(1, 1, 1, 1);        }    }

数字增涨效果:

WorldController增加:public float scoreVisual;在initLevel中添加scoreVisual = score;

在update的最后增加

if (scoreVisual < score)scoreVisual = Math.min(score, scoreVisual+ 250 * deltaTime);

在WorldRenderer里修改:

private void renderGuiScore(SpriteBatch batch) {        float x = -15;        float y = -15;        float offsetX = 50;        float offsetY = 50;        if (worldController.scoreVisual < worldController.score) {            long shakeAlpha = System.currentTimeMillis() % 360;            float shakeDist = 1.5f;            offsetX += MathUtils.sinDeg(shakeAlpha * 2.2f) * shakeDist;            offsetY += MathUtils.sinDeg(shakeAlpha * 2.9f) * shakeDist;        }        batch.draw(Assets.instance.goldCoin.goldCoin, x, y, offsetX, offsetY,                100, 100, 0.35f, -0.35f, 0);        Assets.instance.fonts.defaultBig.draw(batch, ""                + (int) worldController.scoreVisual, x + 75, y + 37);    }

在下一章,我们将使用场景转换效果来平滑的过渡场景

 

0 0
原创粉丝点击