2

我正在尝试编写一个简单的横向卷轴游戏。如果玩家在某个方向上移动得太远,我有这个代码可以让屏幕重新居中。

有没有更好的写法?我觉得我在滥用语言,但效果很好,我认为它可能没问题。

public void adjustFrameIfNecessary()
{
    int dx, dy;
    if ((dx = (GAME_WIDTH - GAME_WIDTH / 3) - player.x) < 0 || (dx = GAME_WIDTH / 3 - player.x) > 0 || (dx = 0) == 0);
    if ((dy = (GAME_HEIGHT - GAME_HEIGHT / 3) - player.y) < 0 || (dy = GAME_HEIGHT / 3 - player.y) > 0 || (dy = 0) == 0);

    if(dx != 0 || dy != 0)
    {
        for (Drawable shiftMe : drawables)
        {
            shiftMe.unconditionalShift(dx, dy);
        }
    }

}

编辑

关于大家的意见,为了使其更具可读性,我已将其更改为

public void adjustFrameIfNecessary()
{
    int dx, dy;

    assignX:
    {
        dx = (GAME_WIDTH - GAME_WIDTH / 3) - player.x;
        if(dx < 0) break assignX;
        dx = GAME_WIDTH / 3 - player.x;
        if(dx > 0) break assignX;
        dx = 0;
    }

    assignY:
    {
        dy = (GAME_HEIGHT - GAME_HEIGHT / 3) - player.y;
        if(dy < 0) break assignY;
        dy = GAME_HEIGHT / 3 - player.y;
        if(dy > 0) break assignY;
        dy = 0;
    }

    if (dx != 0 || dy != 0)
    {
        for (Drawable shiftMe : drawables)
        {
            shiftMe.unconditionalShift(dx, dy);
        }
    }

}

这是否更好?

编辑 2

public void adjustFrameIfNecessary()
{
    int dx = calculateShift(GAME_WIDTH, frameReference.x);
    int dy = calculateShift(GAME_HEIGHT, frameReference.y);

    if (dx != 0 || dy != 0)
    {
        for (Drawable shiftMe : drawables)
        {
            shiftMe.unconditionalShift(dx, dy);
        }
    }
}

我认为现在很清楚。谢谢大家。

4

5 回答 5

5

我不确定我是否完全理解为什么您if在顶部有这些陈述。您应该在初始化变量时简单地分配变量。

dx = (GAME_WIDTH - GAME_WIDTH / 3) - player.x)
dy = (GAME_HEIGHT - GAME_HEIGHT / 3) - player.y)

if无论在您的声明中,他们都将被分配到那个位置。如果它重新满足第三个条件,那么它已经为零,所以没有理由分配它。丢弃这两个if语句,而是坚持直接变量赋值。将条件保持在for循环之上,这是您需要进行的唯一检查。

于 2012-07-14T13:58:00.247 回答
4

您的 if 语句令人困惑。如果您(或其他任何人)需要在以后更改它,这可能非常困难且容易出错。

编写不仅有效而且可维护的代码是一个好习惯。既然你让它工作了,你应该让它更易于维护。

将 if 语句分解为有意义的变量并使用多行。你不是在为高尔夫代码竞赛写作吗?

以后你会感谢自己的。

于 2012-07-14T13:58:19.493 回答
1

这个怎么样?

/* calculate offset to center */
dx = X_current_center - player.x;
dy = Y_current_center - player.y;

if (abs(dx) > MARGIN || abs(dy) > MARGIN)
    /* recenter */
于 2012-07-14T14:00:13.617 回答
1

不可读,但我明白你的意思。不过有一件事:为什么不对 x 和 y 重复使用相同的检查呢?通过使用三元运算符,您可以减少一次黑客攻击。

static int toBounds(int max, int curr) {
  int ret;
  return ((ret = (max - max/3) - curr) < 0 || (ret = max/3 - curr) > 0)? ret : 0;
}
于 2012-07-14T14:24:38.567 回答
1

改进此类代码的想法通常是使用数学:)

首先,对不起,很长的帖子。如果你亲自见到我,不要问我这个话题,我不会停止说话。

简洁版本

做这个:

/**
 * Computes the distance from a point x to an interval [a,b]
 */
public static int distanceToInterval( int x, int a, int b ){
   return x<a? (a-x): ( x>b? (b-x):0 ); 
   // alternative: 
   // return Math.max( a - x, 0 ) + Math.min( b - x, 0 ); 
}

// then use as 
int dx = distanceToInterval( player.x, GAME_WIDTH/3, GAME_WIDTH*2/3 ); 
int dy = distanceToInterval( player.y, GAME_HEIGHT/3, GAME_HEIGHT*2/3 ); 

长版

你的初始陈述

if ((dx = (GAME_WIDTH - GAME_WIDTH / 3) - player.x) < 0 || 
    (dx = GAME_WIDTH / 3 - player.x) > 0 || 
    (dx = 0) == 0);

这可以通过将其变成...来清理一下

...更长的版本

dx1 = GAME_WIDTH*2/3 - player.x; 
dx2 = GAME_WIDTH*1/3 - player.x; 
dx3 = 0; 

if( dx1 < 0 ) dx = dx1; 
else if( dx2 > 0 ) dx = dx2; 
else dx = dx3; 

这更清楚了。我现在可以看到您要做什么,并用简洁的句子表达: 如果玩家不在屏幕的中心三分之一内,我们想移动屏幕

让它更清楚

if( player.x < GAME_WIDTH/3 ) dx = GAME_WIDTH/3 - player.x;  // positive num
else if( player.x > GAME_WIDTH*2/3 ) dx = GAME_WIDTH*2/3 - player.x; // negative num
else dx = 0; // no shift

你可以看到我重新排列了你的陈述,我先检查左边界,然后检查右边界。如果你来到一个从右到左阅读的国家,你可能会发现另一个方向更直观:)

是我会选择的解决方案。它很短,可读,很完美:)

但是,如果你想让它更简洁,你可以更进一步

做数学技巧

您需要了解这三种情况是完全排他的,它们不可能同时发生。让我们回到使用更多变量:

// on the left side we shift a positive number, or not at all 
dx1 = Math.max( GAME_WIDTH/3 - player.x, 0 ); 
// on the right side we shift a negative amount, or not at all 
dx2 = Math.min( GAME_WIDTH*2/3 - player.x, 0 ); 

现在看看它的美:

  • 如果GAME_WIDTH/3-player.x > 0,GAME_WIDTH*2/3 - player.x > 0 那么Math.min(...,0) = 0
  • 如果GAME_WIDTH*2/3 - player.x < 0,GAME_WIDTH*1/3 - player.x < 0 那么Math.max(...,0) = 0

这意味着您可以简单地将两者相加来计算总班次。这就是您的代码的样子:

int dx = 
     Math.max( GAME_WIDTH/3 - player.x, 0 ) + // too far to the left? 
     Math.min( GAME_WIDTH*2/3 - player.x, 0 ); // or too far to the right? 

再抽象一点

无论您选择什么变体,现在都非常容易将其放入方法中。但让我们让它比这个特殊情况更有意义。我会建议

/**
 * Computes the distance from a point x to an interval [a,b]
 */
public static int distanceToInterval( int x, int a, int b ){
   return 
       Math.max( a - x, 0 ) + // too far to the left? 
       Math.min( b - x, 0 ); // or too far to the right? 
}

// now use as 
int dx = distanceToInterval( player.x, GAME_WIDTH/3, GAME_WIDTH*2/3 ); 
int dy = distanceToInterval( player.y, GAME_HEIGHT/3, GAME_HEIGHT*2/3 ); 

ps请注意,我已经在这篇文章中替换GAME_WIDTH - GAME_WIDTH/3了。GAME_WIDTH*2/3这两个给出的整数数学结果略有不同,但我更喜欢简短的版本,因为我发现它更直观(“三分之二的屏幕”与“整个屏幕,然后三分之一”)。

于 2012-07-14T16:24:39.407 回答