OGEngine开发2048游戏及完整源代码揭秘和下载 (一)

使用OGEngine开发2048游戏机源码解析
        最近有一款 2048 的游戏非常火,本文将来介绍一下使用OGEngine 游戏引擎开发游戏2048
OGEngine 引擎是开源的,我们很容易找到,搭建起来也很方便,我们只需在 Android 工程下添加 OGEngine jar 包或者直接引用源码就可以了。


OGEngine引擎官网

2048游戏源码下载

1.创建游戏的主Activity 
创建的游戏主 Activity 入口类继承于 GameActivity 类,需要重写相关方法。
(1) 重写 onCreatePixelPerfectEngineOptions().  此类主要是设置引擎相关参数。
@Override
        protected PixelPerfectEngineOptions onCreatePixelPerfectEngineOptions() {
                PixelPerfectEngineOptions pixelPerfectEngineOptions = new PixelPerfectEngineOptions(
                                this, ZoomCamera.class);
                // 设置竖屏
                pixelPerfectEngineOptions
                                .setScreenOrientation(ScreenOrientation.PORTRAIT_FIXED); 
                // 适配模式,这里设置为“保持宽度不变,改变高”
                pixelPerfectEngineOptions
                                .setPixelPerfectMode(PixelPerfectMode.CHANGE_HEIGHT);
                // 参考尺寸
                pixelPerfectEngineOptions.setDesiredSize(ConstantUtil.DESIRED_SIZE);
                return pixelPerfectEngineOptions;
        }
解析:
① 根据游戏本身的需要设置竖屏或者横屏; ScreenOrientation.PORTRAIT_FIXED  这个参数表示竖屏, ScreenOrientation.LANDSCAPE_FIXED  这参数表上横屏,我这里设置成了竖屏。
② 设置适配模式, PixelPerfectMode.CHANGE_HEIGHT  表上“保持宽度不变,改变高”。
③ 屏幕参考尺寸,我这里是竖屏上面又设置了“保持宽度不变,改变高”,所以我这里的参考尺寸设为 480 ,表示保持镜头的宽为 480 不变,根据实际手机屏幕分辨率的宽高比改变镜头的高。
 
(2) 重写  onLoadResources().  此类主要用于在此加载相关资源。
 
@Override
        protected void onLoadResources() {
                // 加载图片资源
                RegionRes.loadTexturesFromAssets(Res.ALL_XML);
                
                // 加载字体资源
                FontRes.loadFont(128, 128, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 32, true, Color.BLACK, ConstantUtil.FONT_CARD_NUM);
                FontRes.loadFont(128, 128, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL), 25, true, Color.WHITE, ConstantUtil.FONT_SCORE_NUM);

                // 加载音效资源
                SoundFactory.setAssetBasePath("mfx/");
                SoundRes.loadSoundFromAssets(ConstantUtil.SOUND_SELECT, "select.mp3");
                SoundRes.loadSoundFromAssets(ConstantUtil.SOUND_SETPOS, "setpos.mp3");
                SoundRes.loadSoundFromAssets(ConstantUtil.SOUND_MERGE, "merge.mp3");
        }
(3) 重写  onLoadComplete().  此类在上面 onLoadComplete() 方法中加载资源完成后执行,通常此时可以跳转到相关游戏场景。
@Override
        protected void onLoadComplete() {
                // 加载资源完成后
                this.startScene(GameScene.class);
        }
2.创建游戏场景 GameScene 
创建场景的类可以继承于 Scene  类,场景 Scene Entity 的子类,该类用来创建游戏中的场景。 Scene 是屏幕上所有对象的根容器。
onSceneCreate(SceneBundle bundle) 方法里面创建各种实体,比如  EntityGroup Sprite Text Layer
@Override
        public void onSceneCreate(SceneBundle bundle) {
                super.onSceneCreate(bundle);
                initView();
        }
        private void initView() {
                // 游戏背景
                AnimatedSprite game_bg = new AnimatedSprite(0, 0, Res.GAME_BG,
                                getVertexBufferObjectManager());
                this.attachChild(game_bg);
                // 中间游戏主体部分
                mGameGroup = new GameGroup(this);
                // 设置改Group的中心位置在镜头的中心点上
                mGameGroup.setCentrePosition(this.getCameraCenterX(),
                                this.getCameraCenterY());
                this.attachChild(mGameGroup);

                // 2048 LOGO
                AnimatedSprite game_logo = new AnimatedSprite(20, 20, Res.GAME_LOGO,
                                getVertexBufferObjectManager());
                this.attachChild(game_logo);

                // 最佳得分背景
                bestScoreBg = new AnimatedSprite(0, 20, Res.GAME_SCORE_BG_BEST,
                                getVertexBufferObjectManager());
                // 设置bestScoreBg右边x坐标的位置在镜头的右边减20的位置
                bestScoreBg.setRightPositionX(this.getCameraRightX() - 20);
                this.attachChild(bestScoreBg);

                tBestScore = new Text(0, bestScoreBg.getY() + 50,
                                FontRes.getFont(ConstantUtil.FONT_SCORE_NUM),
                                SharedUtil.getBestScore(getActivity()) + "", 4,
                                getVertexBufferObjectManager());
                // 设置 tBestScore 的X坐标上的中点在bestScoreBg的X坐标中点上
                tBestScore.setCentrePositionX(bestScoreBg.getCentreX());
                this.attachChild(tBestScore);

                // 当前得分背景
                currScoreBg = new AnimatedSprite(0, bestScoreBg.getY(),
                                Res.GAME_SCORE_BG_NOW, getVertexBufferObjectManager());
                // 设置currScoreBg的右边X坐标点在bestScoreBg左边的X坐标减20的位置上
                currScoreBg.setRightPositionX(bestScoreBg.getLeftX() - 20);
                this.attachChild(currScoreBg);
.....
        }
(1) Scene  类是游戏中非常重要的一个类,在 Scene 场景中,利用 attachChild(IEntity) 来添加实体。

(2)  GameActivity  类跳转到 Scene  或者 Scene  于  Scene  之间的跳转使用
public Scene startScene(Class<? extends Scene> pSceneCls)
public Scene startScene(Class<? extends Scene> pSceneCls, SceneBundle bundle)
pSceneCls :需要跳转的场景的 Class
bundle 用于传递场景之间的数据

(3) Scene 中包含生命周期,在 Scene 被添加到引擎渲染后、 Activity 执行对应生命周期时、 Scene 会重新显示时执行
public void onSceneCreate(SceneBundle bundle)
public void onSceneResume()
public void onScenePause()
public void onSceneDestroy()

(4)  要关闭一个 Scene  使用  finish()  方法即可。

(5)  需要灵活运用实体中有关设置坐标位置和获取坐标位置的方法来设定实体 Entity 的位置。下面是部分相关方法。
 
        //获取左X坐标
        public float getLeftX() ;
//获取右X坐标
        public float getRightX();
        //设置右上X位置
        public void setRightPositionX(float pX);
//获取底部Y位置
        public float getBottomY();
//设置底部Y位置
        public void setBottomPositionY(float pY) ;
        // 获取中心X坐标
        public float getCentreX() ;
        // 获取中心Y坐标
        public float getCentreY() ;
        // 设置中心X位置
        public void setCentrePositionX(float pCentreX) ;
        // 设置中心Y位置
        public void setCentrePositionY(float pCentreY);
        // 设置中心位置
        public void setCentrePosition(float pCentreX, float pCentreY) ;
(6) 显示、更新分数。这里显示分数使用到了 Text  文本类 tBestScore、tBestScore均为 Text
/**
         * 更新最高纪录
         * 
         * @param pBestScore
         */
        private void updateBestScore(int pBestScore) {
                tBestScore.setText(pBestScore + "");
// 设置居中
                tBestScore.setCentrePositionX(bestScoreBg.getCentreX());
        }

        /**
         * 增加当前分数
         * 
         * @param pAddScore
         *            所增加的分数
         */
        public void addCurrScore(int pAddScore) {
                if (pAddScore != 0) {
                        // 播放音效
                        SoundRes.playSound(ConstantUtil.SOUND_MERGE);
                }
                currScore += pAddScore;
                tCurrScore.setText(currScore + "");
                tCurrScore.setCentrePositionX(currScoreBg.getCentreX());

                // 当前分数大于所保存的最佳分数时,更新最佳分数
                if (currScore > SharedUtil.getBestScore(getActivity())) {
                        SharedUtil.setBestScore(getActivity(), currScore);
                        updateBestScore(currScore);
                }
        }
() 按钮及按钮点击事件监听,btnHelp 和 btnExit 均为 ButtonSprite ,通过
setOnClickListener(onClickListener);  设置点击事件监听。
/**
         * 按钮点击监听
         */
        private OnClickListener onClickListener = new OnClickListener() {

                @Override
                public void onClick(ButtonSprite pButtonSprite, float pTouchAreaLocalX,
                                float pTouchAreaLocalY) {
                        if (pButtonSprite == btnHelp) {
                                // 点击了帮助按钮
                                attachChild(helpLayer);
                        } else if (pButtonSprite == btnExit) {
                                // 点击了退出游戏按钮
                                showDialog();
                        }

                }
        };
3.创建卡片类
手游 2048 中,卡片是一个重要的单元体,移动的其实是卡片,下面我们来介绍一下卡片类的实现。

(1) 稍微扩展一下卡片精灵
我们知道,在 2048 游戏中,每种数字所对应的卡片颜色不一样,这里我们使用卡片的原图是一白色的方块图,通过相关设置颜色的方法可以改变卡片的颜色。观察 OGEngine 引擎源码可以发现 Entity  类里有个  setColor  的方法,它可以使设定的 RGB 颜色值叠加在原图上形成新的颜色,我们这里的原图使用的是白色的方块图,所以我们可以找到想要变成的颜色的图,拿到它的 RGB 值就好办了。
我们可以看一下 Entity  中的  setColor
/**
         * @param pRed
         *            from <code>0.0f</code> to <code>1.0f</code>
         * @param pGreen
         *            from <code>0.0f</code> to <code>1.0f</code>
         * @param pBlue
         *            from <code>0.0f</code> to <code>1.0f</code>
         */
        @Override
        public void setColor(final float pRed, final float pGreen, final float pBlue) {
                if (this.mColor.setChecking(pRed, pGreen, pBlue)) { 
                    this.onUpdateColor();
                }
        }
由上面的源码可以看到, RGB 各个颜色值的范围是 0~1( 如下面的源码注释 ) ,而我们用取色器获取得到的 RGB 0~255 的,不太方便使用,所以我们扩展一下。这里我们在卡片精灵 CardSprite  类中扩展一下。
public class CardSprite extends AnimatedSprite {

        public CardSprite(VertexBufferObjectManager pVertexBufferObjectManager) {
                super(0, 0, Res.GAME_ROUNDRECT, pVertexBufferObjectManager);
        }

        /**
         * 设置RGB 0 到 255
        /**
        public void setRGB(float pRed, float pGreen, float pBlue) {
                this.setColor(pRed / 255, pGreen / 255, pBlue / 255);
        }

        /**
         * 设置数组形式的 RGB 0 到 255
         * @param pRGBs 数组形式的RGB
         */
        public void setRGB(float[] pRGBs) {
                this.setColor(pRGBs[0] / 255, pRGBs[1] / 255, pRGBs[2] / 255);
        }
}
(2) 创建卡片精灵和卡片上面显示的数字的组合类
OGEngine 中要把精灵、文本实体等组合成一个整体我们通常使用到 EntityGroup  类。
public class CardGroup extends EntityGroup {

        private CardSprite cardSprite;// 卡片背景
        private int number = 0;// 数字
        private Text tNum; // 数字文本

        // ============get&set===================
        // 获取数字
        public int getNumber() {
                return number;
        }
        // 设置数字
        public void setNumber(int number) {
                this.number = number;
                onDrawNum(number);
        }
        // ======================================
        public CardGroup(float pX, float pY, Scene pScene) {
// 卡片资源原图的大小为90*90,所以这个组合的宽高可以设置为90*90
                super(pX, pY, 90, 90, pScene);
                // 初始化View
                initView();
                // 自动计算成自适应宽高
                this.setWrapSize();
        }

private void initView() {
                // 创建卡片精灵
                cardSprite = new CardSprite(this.getVertexBufferObjectManager());
                this.attachChild(cardSprite);
                // 创建文本实体用于显示卡片上的数字,文本是可变的,这里设置文本的默认值为空字符串,最大的显示位数为4
                tNum = new Text(0, 0, FontRes.getFont(ConstantUtil.FONT_CARD_NUM), "",
                                4, this.getVertexBufferObjectManager());
                // 设置文本的中心Y坐标在cardSprite的Y坐标中点上
                tNum.setCentrePositionY(cardSprite.getCentreY());
                this.attachChild(tNum);
                // 画卡片上的数字并根据数字显示颜色,默认值为0
                onDrawNum(0);
        }







        // 画卡片上的数字,并根据数字设置卡片的颜色
        private void onDrawNum(int number) {
                float[] mRGBs;
                switch (number) {
                case 0:
                        mRGBs = ConstantUtil.RGBS_0;
                        break;
                case 2:
                        mRGBs = ConstantUtil.RGBS_2;
                        break;
                case 4:
                        mRGBs = ConstantUtil.RGBS_4;
                        break;
                case 8:
                        mRGBs = ConstantUtil.RGBS_8;
                        break;

                // 此次省略了16、32、64、128、256、512 的设置

                case 1024:
                        mRGBs = ConstantUtil.RGBS_1024;
                        break;
                case 2048:
                        mRGBs = ConstantUtil.RGBS_2048;
                        break;
                default:
                        mRGBs = ConstantUtil.RGBS_0;
                        break;
                }
                // 设置精灵颜色,传入的是RGB的数组形式
                cardSprite.setRGB(mRGBs);
                // 设置文本显示,设置数字相对于卡片精灵X坐标居中
                if (number == 0) {
                        tNum.setText("");
                } else {
                        tNum.setText(number + "");
                        tNum.setCentrePositionX(cardSprite.getCentreX());
                }
        }
// 对比当前卡片与传进来的卡片上的数字是否相等
        public boolean equals(CardGroup pCardGroup) {
                return getNumber() == pCardGroup.getNumber();
        }
这样整个卡片类就创建好了,代码注释得比较详细,就不再多解析了。

4.把卡片添加到游戏主体界面部分(GameGroup
上面我们已经创建了卡片类  CardGroup ,下面我们再把它组合成一个整体。
(1)  建立 GameGroup 类,声明一些常量,构造器。
public class GameGroup extends EntityGroup {
        private GameScene mGameScene;
        /**手指滑动的最小响应距离**/
        private final static int FLING_MIN_DISTANCE =10;
        /**卡片之间的间隔**/
        private static final float INTERVAL = 15;
        /**卡片行列数量**/
        private final static int mCount = 4;
        /**卡片尺寸**/
        private final static float CARD_SIZE = 90;
        /**卡片数组**/
        private CardGroup[][] cardArrays = new CardGroup[4][4];
        /**用于标记还有哪些空的位置**/
        private List<Point> emptyPoints = new ArrayList<Point>();
/**随机生成的数字2**/
        private final static int mSamllNum = 2;
/**随机生成的数字4**/
        private final static int mBignum = 4;
        public GameGroup(GameScene pGameScene) {
                super(0, 0, 435, 435, pGameScene);
                // 设置可以监听触碰事件
                this.setIgnoreTouch(false);
                this.mGameScene = pGameScene;
                initView();
        }
(2) 具体实现创建卡片函数,代码如下:
        private void initView() {
                // 创建背景
                AnimatedSprite rectBg = new AnimatedSprite(0, 0, Res.GAME_RECT_BG, this.getVertexBufferObjectManager());
                this.attachChild(rectBg);
                // 创建 4*4 单元格 卡片
                for (int row = 0; row < mCount; row++) {
                        for (int column = 0; column < mCount; column++) {
                                cardArrays[row][column]=new CardGroup((column+1)*INTERVAL+column*CARD_SIZE, (row+1)*INTERVAL+row*CARD_SIZE, getScene());
                                this.attachChild(cardArrays[row][column]);
                                
                        }
                }
                // 在随机一处剩余的空白单元随机生成数字,2或者4
                addRandomNum();
                addRandomNum();
        }
(3)游戏生成随机卡片数字addRandomNum()
/**
         * 在随机一处剩余的空白单元随机生成数字,2或者4
         */
        private void addRandomNum() {
                // 播放音效
                SoundRes.playSound(ConstantUtil.SOUND_SETPOS);
                emptyPoints.clear();
                for (int x = 0; x < mCount; x++) { 
                        for (int y = 0; y < mCount; y++) {
                                if (cardArrays[x][y].getNumber() <= 0) {
                                        emptyPoints.add(new Point(x, y));
                                }
                        }
                }
                Point p = emptyPoints
                                .remove((int) (Math.random() * emptyPoints.size()));
                cardArrays[p.x][p.y].setNumber(Math.random() > 0.1f ? mSamllNum
                                : mBignum);
// 生成卡片的一些动作效果,就是一个由0到1倍的缩放过程
                cardArrays[p.x][p.y].registerEntityModifier(new ScaleModifier(0.2f, 0.0f, 1.0f));
        }
(4) 在游戏场景 GameScene  类中创建 GameGroup
// 中间游戏主体部分
                mGameGroup = new GameGroup(this);
                // 设置改Group的中心位置在镜头的中心点上
                mGameGroup.setCentrePosition(this.getCameraCenterX(),
                                this.getCameraCenterY());
                this.attachChild(mGameGroup);
(5) 运行效果
运行效果如下,中间部分则为 GameGroup  中展示的界面

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值