⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gamemanager.java

📁 J2ME MIDP_Example_Applications
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
// Copyright 2003 Nokia Corporation.
//
// THIS SOURCE CODE IS PROVIDED 'AS IS', WITH NO WARRANTIES WHATSOEVER,
// EXPRESS OR IMPLIED, INCLUDING ANY WARRANTY OF MERCHANTABILITY, FITNESS
// FOR ANY PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE
// OR TRADE PRACTICE, RELATING TO THE SOURCE CODE OR ANY WARRANTY OTHERWISE
// ARISING OUT OF ANY PROPOSAL, SPECIFICATION, OR SAMPLE AND WITH NO
// OBLIGATION OF NOKIA TO PROVIDE THE LICENSEE WITH ANY MAINTENANCE OR
// SUPPORT. FURTHERMORE, NOKIA MAKES NO WARRANTY THAT EXERCISE OF THE
// RIGHTS GRANTED HEREUNDER DOES NOT INFRINGE OR MAY NOT CAUSE INFRINGEMENT
// OF ANY PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OWNED OR CONTROLLED
// BY THIRD PARTIES
//
// Furthermore, information provided in this source code is preliminary,
// and may be changed substantially prior to final release. Nokia Corporation
// retains the right to make changes to this source code at
// any time, without notice. This source code is provided for informational
// purposes only.
//
// Nokia and Nokia Connecting People are registered trademarks of Nokia
// Corporation.
// Java and all Java-based marks are trademarks or registered trademarks of
// Sun Microsystems, Inc.
// Other product and company names mentioned herein may be trademarks or
// trade names of their respective owners.
//
// A non-exclusive, non-transferable, worldwide, limited license is hereby
// granted to the Licensee to download, print, reproduce and modify the
// source code. The licensee has the right to market, sell, distribute and
// make available the source code in original or modified form only when
// incorporated into the programs developed by the Licensee. No other
// license, express or implied, by estoppel or otherwise, to any other
// intellectual property rights is granted herein.


import java.util.Vector;
import javax.microedition.lcdui.*;


// GameManager is used by CloseableCanvas or NokiaCloseableCanvas.
// (BlockGameMIDlet creates a CloseableCanvas or NokiaCloseableCanvas
// depending on the capabilities of the MIDP device where the MIDlet
// was downloaded.)
//
// This GameManager tries to be as portable as possible. It doesn't
// use any drawing feature of the Nokia UI API's FullCanvas that an
// ordinary MIDP Canvas API doesn't support, other than the basic
// 'full canvas' property of FullCanvas.

class GameManager
    implements Runnable
{
    // When either side gets GAME_OVER_SCORE points, the game ends.
    final static int GAME_OVER_SCORE = 100;
    final static int MILLIS_PER_TICK = 250; // msec
    final static int MAX_CHANNELS = 8; // 8 channels of blocks
    final static int MAX_BLOCK_HEIGHT = 10; // 10 pixels
    final static int MAX_PLAYER_LIVES = 5; // 5 lives

    private final static int MAX_PIXELS = 200;
    private final static Font GAME_FONT =
        Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);

    private final int gameWidth;
    private final int gameHeight;
    private final BlockGameMIDlet midlet;
    private final Dictionary dict;
    private final Canvas canvas;
    private final Vector blocks = new Vector();
    private final Base base;
    private final DoublyLinkedList bullets = new DoublyLinkedList();
    private final GameEffects gameEffects;
    private final boolean useLimitedFiringRate;

    private volatile Thread animationThread = null;
    // hasBeenShown could be set by Canvas.showNotify()
    private boolean hasBeenShown = false;
    private volatile boolean isPaused = false;
    private boolean isGameOver = false;
    private int gameOverTicks = 0;
    private int baseScore = 0;
    private int blocksScore = 0;

    // The text labels for the player's score and the base's 'lives count',
    // take up a bit too much screen space on small displays. As the label
    // text itself is rather static, we only show the label text for
    // 'showLabelTicks' (7.5 seconds). Similarly, the base's 'lives count'
    // also changes slowly.
    private int showLabelTicks = (7500 / GameManager.MILLIS_PER_TICK);


    GameManager(BlockGameMIDlet midlet, Dictionary dict,
        GameEffects gameEffects, Canvas canvas)
    {
        this.midlet = midlet;
        this.dict = dict;
        this.canvas = canvas;
        this.gameEffects = gameEffects;

        useLimitedFiringRate = midlet.useLimitedFiringRate();

        // Limit the maximum size of the game to be 200 x 200 pixels.
        // This is for better game play on MIDP devices with a larger
        // resolution. (For example on devices which are very much wider
        // than long, it otherwise takes too long for the blocks to fly from
        // right to left, and for the bullets to fly from left to right.)
        gameWidth  = (canvas.getWidth() < MAX_PIXELS) ?
                     canvas.getWidth() : MAX_PIXELS;
        gameHeight = (canvas.getHeight() < MAX_PIXELS) ?
                     canvas.getHeight() : MAX_PIXELS;


        // A block is a square that is 'dimension' pixels high
        // and 'dimension' pixels wide. The dimension of a block
        // is one of the basic properties which affects how the game
        // looks. (Note: the base's size also depends on this parameter
        // as we'd like the base and blocks to be about the same size.)
        //
        // The screen height is divided into 'MAX_CHANNELS' or less channels.
        // The blocks fly left down the channels. The following figure
        // helps to illustrate:
        //   --------------------------------------------
        //      yOffset |                       ^
        //              +---+  ^                | channelHeight
        //              +   +  | blockHeight    |
        //              +---+  v                |
        //                                      v
        //   ----------------------------------------------
        // There is one block per channel.

        int numChannels = MAX_CHANNELS;

        if (gameHeight < gameWidth)
        {
            numChannels = (gameHeight * numChannels) / gameWidth;
        }

        int channelHeight = gameHeight / numChannels;
        // Set the blockHeight to be 70 % of channelHeight
        // to leave some space between blocks.
        int blockHeight = ((70 * channelHeight) / 100);
        int yOffset = (channelHeight - blockHeight) / 2;

        if ((numChannels < MAX_CHANNELS) &&
            (blockHeight < MAX_BLOCK_HEIGHT))
        {
            // For smaller screens, the blockHeight (dimension) should
            // be at least some minimum pixel size, otherwise the blocks
            // and base feel too small.

            // channel height equals block height
            blockHeight = MAX_BLOCK_HEIGHT;
            numChannels = gameHeight / blockHeight;
            yOffset = 0;
        }
        Block.setDimension(blockHeight); // set dimension for all Blocks


        // Picking a dx value that is independent of gameWidth + blockWidth
        // and that works well for a variety of screen sizes is a bit tricky,
        // because for the smallest device screen widths the game uses
        // a minimum pixel size (which may also reduce the number
        // of channels) rather than a constant ratio of blockWidth to
        // gameWidth for all screen sizes. Also, on taller screens
        // the user may have to spend more time moving up and down
        // than on shorter screens (e.g. if there are 8 channels of blocks
        // rather than 6 channels on a smaller device). This is result of not
        // making the game scale proportional to the height and width of
        // the screen, for all possible screen sizes.
        //
        // The block speed should not be either annoyingly fast or slow
        // on a range of real devices.

        int dx;

        if (numChannels < MAX_CHANNELS)
        {
            dx = -(gameWidth / (3 * Block.getDimension()));
        }
        else
        {
            dx = -(gameWidth / (4 * Block.getDimension()));
        }
        if (dx == 0)
        {
            dx = -1;
        }

        for (int channel = 0; channel < numChannels; channel++)
        {
            Block block = new Block(this, 0, gameWidth,
                (yOffset + (channel * channelHeight)), dx);

            blocks.addElement(block);
        }

        int playerLives = MAX_PLAYER_LIVES;

        base = new Base(this, useLimitedFiringRate, playerLives, 0, 0,
                        gameWidth, gameHeight);
    }


    boolean isGameOver()
    {
        return isGameOver;
    }


    private boolean baseIsWinning()
    {
        return ((base.getLives() > 0) && (baseScore >= blocksScore));
    }


    private void tick()
    {
        if (isGameOver)
        {
            // 1) The game is over.

            // When the game is over, wait 30 seconds before stopping the
            // animation thread. This gives the draw method a chance to wait
            // a few seconds before displaying a message that prompts the user
            // to press a softkey to return to the MainMenu. It also gives the
            // 'game over' tune some time to play.)
            if (gameOverTicks < (30000 / MILLIS_PER_TICK))
            {
                gameOverTicks++;

                // When the game is over, wait 1 second before playing
                // the game over music
                if (gameEffects.hasSoundCapability() &&
                    (gameOverTicks == (1000 / MILLIS_PER_TICK)))
                {
                    boolean hasPlayerWon = (baseScore >= blocksScore);

                    gameEffects.playGameOverMusic(hasPlayerWon);
                }
            }
            else
            {
                // 30 seconds has passed, stop the animation thread, etc.
                stop();
            }
        }
        else if ((base.getLives() == 0) || (baseScore >= GAME_OVER_SCORE) ||
            (blocksScore >= GAME_OVER_SCORE))
        {
            // 2) Detect and set the 'game over' state.

            // Setting isGameOver to 'true' causes the 'if (isGameOver)'
            // block of code above, to be executed on subsequent ticks.

            isGameOver = true;
            gameOverTicks = 0;
        }
        else
        {
            // 3) The game is still playing.

            // showLabelTicks is used by the draw method to print
            // longer or shorter text messages indicating the current score,
            // lives, etc. When the game first starts, longer versions
            // (with explanatory labels) are printed. When the tick
            // counts reach zero, shorter versions are printed
            // (so more of the screen is visible during playing of the game).
            if (showLabelTicks > 0)
            {
                showLabelTicks--;
            }

            // Base tick
            base.tick();


            // Bullets' ticks
            Bullet b = (Bullet) (bullets.getFirst());
            Bullet prev = null;

            while (b != null)
            {
                b.tick();
                prev = b;
                b = (Bullet) (bullets.getNext(b));
                if (!prev.isActive())
                {
                    bullets.remove(prev);
                }
            }

            // Blocks' ticks
            for (int ix = 0; ix < blocks.size(); ix++)
            {
                Block block = (Block) blocks.elementAt(ix);

                block.tick();

                // Check for bullet collisions
                b = (Bullet) (bullets.getFirst());
                while (b != null)
                {
                    if (block.isCollision(b))
                    {
                        if (block.doBulletCollision())
                        {
                            // true: the block exploded in the collision

                            baseScore += block.getPoints();
                            block.updateStrength(); // stronger next life
                            gameEffects.playBlockExplosion();
                        }
                        b.doExplode();
                    }
                    b = (Bullet) (bullets.getNext(b));
                }

                // Check for base collisions
                if (block.isCollision(base))
                {
                    int lives = base.getLives();

                    if (!base.isColliding())
                    {
                        // If the base is not already colliding with
                        // another block and is colliding with this block,
                        // then handle the base collision:
                        //   - inform base of collision
                        //   - get new 'base lives' count
                        //   - use GameEffects for explode noise + vibrate
                        base.doCollision();
                        gameEffects.playBaseExplosion();
                        gameEffects.vibrate();
                    }

                    // Handle block collision:
                    //   Blocks get more points for hitting the base,
                    //   than vice versa. Blocks get extra points
                    //   for completely destroying base (no lives left).
                    baseScore += block.getPoints();
                    if (lives == 0)
                    {
                        blocksScore += 20;
                    }
                    else
                    {
                        blocksScore += (2 * block.getPoints());
                    }
                    block.doExplode();
                    block.updateStrength(); // A stronger next life
                }
            }
        }
    }


    // Canvas methods

    public void paint(Graphics g)
    {
        if (isGameOver)
        {
            // Print an appropriate 'game over' message.

            int color;
            String winnerText;

            if (baseIsWinning())
            {
                color = Base.COLOR;
                winnerText = dict.getString(Dictionary.TEXT_GAME_YOU_WON);
            }
            else
            {
                color = Block.COLOR;
                winnerText = dict.getString(Dictionary.TEXT_GAME_YOU_LOST);
            }


            String lastText = null; // last line's default message

            if (gameOverTicks < (4000 / MILLIS_PER_TICK))
            {
                if (base.getLives() == 0)
                {
                    lastText =
                        dict.getString(Dictionary.TEXT_GAME_BASE_DESTROYED);
                }
            }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -