【问题标题】:How should i be adding/loading images into my game?我应该如何在我的游戏中添加/加载图像?
【发布时间】:2013-12-13 13:44:34
【问题描述】:

所以,和其他人一样,我一直在使用 libgdx 开发一款针对 android 操作系统的游戏。我真的很想使用我的个人艺术(photoshop 画笔)而不是像素艺术。好吧,实际上我正在尝试融合 2,因为我们都知道 android 无法容纳足够的内存来显示我想使用的一些艺术作品。

例如:我有许多 512x512 的背景并排放置以构成整个“第一阶段”,看似多达 12 张以上的图像,每张图像约为 100k。这对 android 造成了负担,并显着降低了帧速率,低于 30 fps,使我无法向场景中添加更多图像,而我将 需要 这样做。

问题:添加“背景”图像和背景装饰物的方法是什么?

他们不应该超过多少 Kb(大小)?

我将它们作为 Sprite 而不是纹理加载,是好还是坏?

背景图片保存为 .png 512x512 8bit,是好还是坏,哪个更好?

texturePacker 是否对性能有任何帮助,我是否应该对这些 512x512 或更大的文件进行纹理包装?

顺便说一句,我没有打包背景图片...

我是否必须求助于像素艺术和较小的图像尺寸才能在 android 上创建一个成熟的横向滚动条?

我知道有很多问题,我试图解释我的困境,这样你就会从整体上理解它...... 将所有这些问题视为 1 个问题:

我已经在这个游戏上工作了 7 个月了,我知道我必须在某个时候穿过这座桥,我已经完成了很多,我会说引擎已经接近完成,意思是地图/游戏编辑器,角色机制/能力、游戏机制(关卡障碍/要做的事情等)、goomba 和老板机制/能力/人工智能、故事、前景动画(64x64、64x128 图像没问题,即小文件大小)、项目、菜单、IT 都准备好了除了我有多个大背景尺寸的问题外,我都使用了....

感谢任何链接、文章、教程...我已经知道如果我做错了什么我可能不得不重做很多东西...

我要玩一个完整的 20 多小时横向卷轴游戏,在这么大的游戏中,我需要在 gfx 方面做什么?

【问题讨论】:

  • 我显示的图像远大于在极低规格设备上显示的图像。您确定不是每帧都重新加载图像吗?
  • 是的,它们只加载一次以确保我理解你的意思,当我加载纹理时,我使用这个 sn-p: pangodavillageBG01 = new Sprite(new Texture(Gdx.files.internal("数据/背景/pangodavillageBG01.png"))); -------> pangodavillageBG01.setBounds(0, 0, 25, 25);我放了一个 Gdx.app.log("Loadtexture:", "xxx") 并且它只在控制台中显示一次......
  • 是否所有的背景图像即使不在屏幕上也会被绘制?我看不出你为什么会遇到这样的 FPS 损失。
  • 哦该死的你对,我还没有实现任何剪辑,这不就是所谓的“剪辑”吗?可能就是这样,我开始四处寻找一些信息,你不会有任何资源链接吗? tyvm...
  • 呃剪辑没有改变任何东西

标签: android image textures libgdx


【解决方案1】:

你的drawblocks函数每次调用它时都会调用新的textureregion,你每次都在创建一个新对象。您应该只调用一次 new 来创建对象,并在调用 drawblocks 时引用该对象。

创建某种在创建屏幕时调用的 init 函数。

TextureRegion block1 = new TextureRegion(blockTextureRegion);

然后调用drawblocks函数。

case 0: spriteBatch.draw(block1, block.getPosition...

这样你只调用一次 new 而不是每次调用 drawblocks。

【讨论】:

    【解决方案2】:

    你应该阅读这个https://code.google.com/p/libgdx/wiki/TexturePacker

    使用

    batch.begin();
    

    你的精灵渲染代码在这里

    batch.end();
    

    这将帮助您获得更高的 fps

    纹理打包器确实提高了使用它的性能

    【讨论】:

    • 所以我用纹理打包器打包了图像(BGs),我已经使用纹理打包器和其他东西而不是背景,并且帧率徘徊在 30 fps 以下,我还应该提到图像像视差背景一样相对于玩家位置移动,除了它只在玩家移动时移动(我没有使用臭名昭著的视差类),我有 8 个 512x512 图像,大小为 30kb,一层有 4 个,前面有 4 个,还有 250 个 64x64 8kb 的图块显示在屏幕和播放器上,所以对我来说它似乎应该以 60fps 运行......还有其他想法吗?伙计们
    • 分享你的渲染代码,这样我可以更好地了解你在做什么
    【解决方案3】:

    这是未经编辑的代码,因此您可以了解一下,我使用的是 WVC 方法,这是主要的渲染方法,如果您想查看此处调用的方法,请告诉我.. 我知道代码需要优化,所以如果您看到任何重大问题,请让我知道我应该做什么.. ty

    public void render() {
     player01_class player01 = world.getPlayer01();
    
     if ((player01.getPosition().x) > (CAMERA_WIDTH / 2))  {
         cam.position.set((player01.getPosition().x), (cam.position.y), 0);
         //cam.zoom = 3f;
         cam.update();  
    
    
     } else if ((player01.getPosition().x) < (CAMERA_WIDTH / 2))  {
         cam.position.set((player01.getPosition().x), (cam.position.y), 0);
         //cam.zoom = 3f;
         cam.update();           
     }
    
     if ((player01.getPosition().y) > (CAMERA_HEIGHT / 2)) {
         cam.position.set((cam.position.x), (player01.getPosition().y) , 0);
         //cam.zoom = 3f;
         cam.update();  
     } else if ((player01.getPosition().y) < (CAMERA_HEIGHT) / 2) {
         cam.position.set((cam.position.x), (cam.position.y) , 0);
         //cam.zoom = 3f;
         cam.update();  
    
     }
    
    
     clipBounds = new Rectangle(cam.position.x - 10, cam.position.y - 10, cam.viewportWidth, cam.viewportHeight);
    
     ScissorStack.calculateScissors(cam, spriteBatch.getTransformMatrix(), clipBounds, scissors);
     ScissorStack.pushScissors(scissors);
    
     spriteBatch.setProjectionMatrix(cam.combined);  
     spriteBatch.begin();
    
    
     //bg_pilot.setPosition(player01.getPosition().x / 1.05f - 10, 0);
     //bg_pilot.draw(spriteBatch, 1f);
    
    
     pangodavillageBG_x = player01.getPosition().x;
    
     spriteBatch.draw(new TextureRegion(pangodavillageBG01), pangodavillageBG_x / 1.80f, 2 , 25,  25);   
     spriteBatch.draw(new TextureRegion(pangodavillageBG02), pangodavillageBG_x / 1.80f + 25, 2 , 25,  25);  
     spriteBatch.draw(new TextureRegion(pangodavillageBG03), pangodavillageBG_x / 1.80f + 50, 2 , 25,  25);  
     spriteBatch.draw(new TextureRegion(pangodavillageBG04), pangodavillageBG_x / 1.80f + 75, 2 , 25,  25);  
    
     spriteBatch.draw(new TextureRegion(pangodavillageFG01), pangodavillageBG_x / 2.00f, 0 , 25,  25);   
     spriteBatch.draw(new TextureRegion(pangodavillageFG02), pangodavillageBG_x / 2.00f + 25, 0 , 25,  25);  
     spriteBatch.draw(new TextureRegion(pangodavillageFG03), pangodavillageBG_x / 2.00f + 50, 0 , 25,  25);  
     spriteBatch.draw(new TextureRegion(pangodavillageFG04), pangodavillageBG_x / 2.00f + 75, 0 , 25,  25);  
    
    
    
    // drawClouds();   
    
     UpdateTime();
    
     drawBlocks(); 
    
     //if (controller.edit_toggle)  {
     drawEditBlocks();
     //}
    
     drawItems(); 
    
     drawPowerUps();
    
     drawPlayer01();
    
     drawBadGuy();
    
     drawUIItems();
    
    
    
     //stage.act(Gdx.graphics.getDeltaTime());
    // stage.draw();
    
    
     bmpfont.draw(spriteBatch, "FPS: " +  Gdx.graphics.getFramesPerSecond(),  cam.position.x - 10,  cam.position.y + 7);
     bmpfont.drawMultiLine(spriteBatch, "playerX: " + (int)player01.getPosition().x + " Y: " + (int)player01.getPosition().y, cam.position.x - 10,  cam.position.y + 6, 1f, HAlignment.LEFT); 
    // bmpfont.drawMultiLine(spriteBatch, "rx1: " + rectx1 + " ry1: " + recty1,  cam.position.x - 10,  cam.position.y + 5, 1, HAlignment.LEFT);
    // bmpfont.drawMultiLine(spriteBatch, "block: " + currentBlock_counter, cam.position.x - 10,  cam.position.y + 5, 1, HAlignment.LEFT);
    
     //bmpfont.drawMultiLine(spriteBatch, "badx: " + badGuy.getPosition().x + " badguyy: " +  badGuy.getPosition().y,  cam.position.x - 10,  cam.position.y + 4, 1, HAlignment.LEFT);    
     //bmpfont.drawMultiLine(spriteBatch, "cvelocity: " + badGuy.getAcceleration() + " VBGy: " + badGuy.blockCollidedwithBGY , cam.position.x - 10,  cam.position.y + 3, 1f, HAlignment.LEFT); 
     bmpfont.drawMultiLine(spriteBatch, "mouseX: " + mouseX + " Y: " + mouseY  , cam.position.x - 10, cam.position.y + 5, 1f, HAlignment.LEFT); 
     //bmpfont.drawMultiLine(spriteBatch, "mousexX: " + mouseRawX + " Y: " + mouseRawY  , cam.position.x - 10, cam.position.y + 3, 1f, HAlignment.LEFT); 
    
     bmpfont.drawMultiLine(spriteBatch, "camposX: " + (int)cam.position.x + " camposY: " + (int)cam.position.y, cam.position.x - 10, cam.position.y + 4, 1f, HAlignment.LEFT);
    
     bmpfont.drawMultiLine(spriteBatch, "H: " + WorldTime_hour + " M: " + WorldTime_min + " S: " + (int)WorldTime_sec, cam.position.x + 6, cam.position.y + 7, 1f, HAlignment.LEFT);
    
     //bmpfont.drawMultiLine(spriteBatch, "TileNumber: " + mapTile_ID + "  block_collide_bool: " +  map_block_collide_bool, cam.position.x - 10, cam.position.y + 3, 1f, HAlignment.LEFT); 
    
     //bmpfont.drawMultiLine(spriteBatch, " aY: " + addButtonRect, cam.position.x, cam.position.y + 2, 1f, HAlignment.LEFT);        
    // bmpfont.drawMultiLine(spriteBatch, "m: " + rect_mouse + "  a: " + addButtonRect, cam.position.x - 10, cam.position.y + 2, 1f, HAlignment.LEFT);      
     //bmpfont.drawMultiLine(spriteBatch, "mapTileID: " + mapTile_ID, cam.position.x - 10, cam.position.y + 2, 1f, HAlignment.LEFT);        
    
    // bmpfont.drawMultiLine(spriteBatch, "Game Mode: " + edit_toggle , cam.position.x - 10, cam.position.y + 1, 1f, HAlignment.LEFT);      
    
    
     spriteBatch.end();
    
     if (debug) {
         drawDebug();
         //drawCollisionBlocks(); 
        // drawCollisionToggleBlocks();
    
         drawEditMenu();
    
    
    }
    
     spriteBatch.begin();
    
       //updateParticles();
    
     spriteBatch.end();
    
    // spriteBatch.setProjectionMatrix(cam.combined);  
     spriteBatch.begin();
    
     bmpfont.setScale(.03f); 
     bmpfont.setColor(new Color(1,1,1,1));
    
     bmpfont.draw(spriteBatch, "ADD", cam.position.x - 9.5f, cam.position.y + 9.65f);
     bmpfont.draw(spriteBatch, "DEL", cam.position.x - 7.9f, cam.position.y + 9.65f);
    
     bmpfont.draw(spriteBatch, "MODE", cam.position.x - 5.4f, cam.position.y + 9.65f);
    
     bmpfont.draw(spriteBatch, "BLK", cam.position.x - 2.9f, cam.position.y + 9.65f);
     bmpfont.draw(spriteBatch, "ITM", cam.position.x - 1.35f, cam.position.y + 9.65f);
     bmpfont.draw(spriteBatch, "BAD", cam.position.x - -0.1f, cam.position.y + 9.65f);
    
     spriteBatch.end();
    
     spriteBatch.flush();
     ScissorStack.popScissors();
    
    
     bmpfont.setColor(new Color(1,1,1,1));
     bmpfont.setScale(.04f); 
    

    }

    这里是 drawBlocks 方法:

      private void drawBlocks()  {
    
       player01_class player01 = world.getPlayer01();
    
    
       displayBlock_x[3] = player01.getPosition().x;
       displayBlock_y[3] = cam.position.y + 8;
    
    
       for (Block block : world.getBlocks()) {
    
     //spriteBatch.draw(blockTextureRegion, block.getPosition().x * ppuX, block.getPosition().y * ppuY, Block.SIZE * ppuX, Block.SIZE * ppuY);
    
       switch (block.blockID)
    
        {
    
        case 0:
              spriteBatch.draw(new TextureRegion(blockTextureRegion), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 1:
              spriteBatch.draw(new TextureRegion(blockTextureRegion_metal), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 2:
              spriteBatch.draw(new TextureRegion(btr_basket), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 3:
              spriteBatch.draw(new TextureRegion( btr_sand ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
        case 4:
              spriteBatch.draw(new TextureRegion( btr_stone1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 5:
              spriteBatch.draw(new TextureRegion( btr_brick ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 6:
              spriteBatch.draw(new TextureRegion( btr_jewel ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 7:
              spriteBatch.draw(new TextureRegion(btr_waterlike), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 8:
              spriteBatch.draw(new TextureRegion(btr_dirtfull1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 9:
              spriteBatch.draw(new TextureRegion( btr_dirtleftside1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 10:
              spriteBatch.draw(new TextureRegion(btr_dirtrightside1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 11:
              spriteBatch.draw(new TextureRegion( btr_dirttop1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 12:
              spriteBatch.draw(new TextureRegion( btr_dirt_leftgrassend1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 13:
              spriteBatch.draw(new TextureRegion( btr_dirt_rightgrassend1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
        case 14:
              spriteBatch.draw(new TextureRegion( btr_grass1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 15:
              spriteBatch.draw(new TextureRegion(btr_grass_leftend1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 16:
              spriteBatch.draw(new TextureRegion( btr_grass_rightend1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 17:
              spriteBatch.draw(new TextureRegion(btr_grasspatch1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 18:
              spriteBatch.draw(new TextureRegion(sand_full_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());               
        break;
    
        case 19:
              spriteBatch.draw(new TextureRegion(marble_white_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 20:
              spriteBatch.draw(new TextureRegion(marble_darkwhite_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 21:
              spriteBatch.draw(new TextureRegion(stone_darkgrey_2), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 22:
              spriteBatch.draw(new TextureRegion( stone_darkgrey_3 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
        case 23:
              spriteBatch.draw(new TextureRegion( stone_grey_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 24:
              spriteBatch.draw(new TextureRegion( stone_grey_2 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 25:
              spriteBatch.draw(new TextureRegion( stoneinset_grey_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 26:
              spriteBatch.draw(new TextureRegion(stoneinset_darkgrey_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 27:
              spriteBatch.draw(new TextureRegion(stoneoutset_grey_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 28:
              spriteBatch.draw(new TextureRegion( stoneoutset_darkgrey_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 29:
              spriteBatch.draw(new TextureRegion(stone_decogrey_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 30:
              spriteBatch.draw(new TextureRegion( stone_decodarkgrey_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 31:
              spriteBatch.draw(new TextureRegion( stone_decogrey_2 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 32:
              spriteBatch.draw(new TextureRegion( stone_decodarkgrey_2 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 33:
              spriteBatch.draw(new TextureRegion( stone_decoblue_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 34:
              spriteBatch.draw(new TextureRegion(stone_decodarkblue_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 35:
              spriteBatch.draw(new TextureRegion( stone_decogreen_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 36:
              spriteBatch.draw(new TextureRegion(stone_decodarkgreen_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 37:
              spriteBatch.draw(new TextureRegion(stone_decoyellow_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());            
        break;
    
        case 38:
              spriteBatch.draw(new TextureRegion( stone_decodarkyellow_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 39:
              spriteBatch.draw(new TextureRegion( stone_decoorange_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 40:
              spriteBatch.draw(new TextureRegion( stone_decodarkorange_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
        case 41:
              spriteBatch.draw(new TextureRegion( stone_decopurple_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 42:
              spriteBatch.draw(new TextureRegion(stone_decodarkpurple_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 43:
              spriteBatch.draw(new TextureRegion( woodpanel_top_1 ), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 44:
              spriteBatch.draw(new TextureRegion(woodpanel_mid_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
        break;
    
        case 45:
              spriteBatch.draw(new TextureRegion(woodpanel_bottom_1), block.getPosition().x , block.getPosition().y , block.getSize(), block.getSize());   
    
        break;
        }
    
      }
    
          if (currentBlock_counter > 45)  {
    
       currentBlock_counter = 0;
    
          }
    
        }  
    

    这里是drawPlayer方法:

    private void drawPlayer01()  {
      player01_class player01 = world.getPlayer01();
    
    
    player01Frame = player01.isFacingLeft() ? playerIdleLeft : playerIdleRight;
    
    if(player01.getState().equals(State.WALKING)) {
    
        player01Frame = player01.isFacingLeft() ?        walkLeftAnimation.getKeyFrame(player01.getStateTime(), true) : 
    
        walkRightAnimation.getKeyFrame(player01.getStateTime(), true);
    
    } else if (player01.getState().equals(State.JUMPING)) { 
    
        if (player01.getVelocity().y > 0) { 
            player01Frame = player01.isFacingLeft() ? player01JumpLeft : player01JumpRight;     
        } else {    
            player01Frame = player01.isFacingLeft() ? player01FallLeft : player01FallRight;     
        }
    
    }  else if (player01.getState().equals(State.ATTACKING)) {  
    
        player01Frame = player01.isFacingLeft() ? playerAtkLeft : playerAtkRight;
    
    }
    
    //spriteBatch.draw(player01Frame, player01.getPosition().x * ppuX, player01.getPosition().y * ppuY, player01.SIZE * ppuX, player01.SIZE * ppuY);
    
    //Attack 
    if (player01.boolWeapon_isStriking)  {
        int a = 0;
    
        player01Frame = player01.isFacingLeft() ? playerAtkLeft : playerAtkRight;       
        sword01Frame = player01.isFacingLeft() ? sword01L : sword01R;       
    
        //if(System.currentTimeMillis() - lastPressProcessed > 20) {
    
            if (!player01.isFacingLeft()) {
    
                player01.weaponPosition.set(player01.getPosition().x + 1, player01.getPosition().y);
    
            } else if (player01.isFacingLeft()){
    
                //player01Frame = player01.isFacingLeft() ? sword01L : sword01R;        
                player01.weaponPosition.set(player01.getPosition().x - 1.5f, player01.getPosition().y);
    
            }
    
         //spriteBatch.draw(player01Frame, player01.getPosition().x, player01.getPosition().y, player01.getSizeW() , player01.getSizeH() );
    
         spriteBatch.draw(new TextureRegion(sword01Frame), player01.weaponPosition.x, player01.weaponPosition.y - 0.00f, player01.getWeaponSizeW() , player01.getWeaponSizeH()); 
    
    }
    
    spriteBatch.draw(player01Frame, player01.getPosition().x, player01.getPosition().y, player01.getSizeW() , player01.getSizeH() );
    

    }

    【讨论】:

      【解决方案4】:

      就像之前提到的那样.. 必须删除 draw() 方法中的任何“新”语句! 这绝对是你的问题的原因!

      你必须明白每帧都会调用draw方法!因此,如果您多次分配 40kb 大小的纹理 - 每秒 60 次,这会占用大量内存带宽!

      那里也存在严重的内存泄漏,因为每一帧你都会创建一个纹理区域并且不调用 .dispose()。

      您的方法是在类 init(例如在构造函数中)创建纹理区域并将它们存储在私有 TextureRegion 属性中,然后使用它们..

      在整个绘制方法中再次没有新的关键字!这也适用于新的 Color(1,1,1,1) .. 改用 priv 属性并分配一次或使用 Color.WHITE..

      你使用的所有字符串也是如此!没有 + 的字符串允许.. 也没有 .toString() e.t.c.

      为了偏执,你甚至不应该在 draw 中声明一个 float/int 属性。创建一个私有和重用...

      【讨论】:

        最近更新 更多