为什么Color.colorToHSV和HSVToColor不准确?

时间:2015-09-12 13:43:04

标签: java android colors

我正在写一款Android游戏。在游戏中,不同颜色的块会掉到地上。当具有相同颜色的两个块彼此叠加时,它们将合并为一个颜色较深的块。但是,我观察到具有相同颜色的块有时不会合并。经过一些调试后,我发现它们实际上颜色略有不同:-16757700和-16757444。我使用Color.colorToHSVColor.HSVToColor使颜色变深,如下面的代码所示(checkLanded方法)。所以我认为这两种hsv方法一定有问题。这是我的代码(实际上我不认为代码有任何其他问题,你可以看一下checkLanded方法。但是我粘贴了其他代码,因为问题可能不在我认为的地方几乎滚动到底部,你会看到checkLanded方法):

package com.shades;

import android.graphics.Color;
import android.support.annotation.IntDef;

import com.shades.util.BlockView;
import com.shades.util.Timer;

import java.util.HashMap;
import java.util.Random;

public class Block {

    private static Random r = new Random ();
    private static HashMap<Integer, Float> colorValuesMap;

    @IntDef ({LEFT, RIGHT})
    public @interface Direction {}
    public static final int LEFT = -1;
    public static final int RIGHT = 1;

    static {
        colorValuesMap = new HashMap<> ();
        colorValuesMap.put (4, 0.1F);
        colorValuesMap.put (3, 0.3F);
        colorValuesMap.put (2, 0.5F);
        colorValuesMap.put (1, 0.7F);
        colorValuesMap.put (0, 0.9F);
    }

    private int x;
    private int y;
    private int color;
    protected Timer timer;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    private void setY(int y) {
        Block[][] matrix = Game.getInstance ().getBlockMatrix ();
        matrix[this.x][this.y] = null;
        this.y = y;
        matrix[this.x][this.y] = this;
        BlockView.updateViews ();
    }

    private void setX(int x) {
        Block[][] matrix = Game.getInstance ().getBlockMatrix ();
        matrix[this.x][this.y] = null;
        this.x = x;
        matrix[this.x][this.y] = this;
        BlockView.updateViews ();
    }

    private void setXY (int x, int y) {
        Block[][] matrix = Game.getInstance ().getBlockMatrix ();
        matrix[this.x][this.y] = null;
        this.y = y;
        this.x = x;
        matrix[x][y] = this;
        BlockView.updateViews ();
    }

    public int getColor () {
        return color;
    }

    public Block(int x, int y) {
        setXY (x, y);
        this.y = y;
        float[] hsv = new float[3];
        hsv[0] = Game.getInstance ().getHueNumber ();
        hsv[1] = 1F;
        hsv[2] = colorValuesMap.get (r.nextInt (4));
        color = Color.HSVToColor (hsv);
        BlockView.updateViews ();
        timer = new Timer (new Runnable () {
            @Override
            public void run() {
                moveDown ();
            }
        }, 1000, true);
    }

    private void moveDown () {

        //if reached bottom
        if (getY () == Game.MATRIX_HEIGHT - 1) {
            timer.stopTimer ();
            Game.getInstance ().nextBlock (r);
            return;
        }
        color = Game.getInstance ().getBlockViewMatrix ()[x][y].getColor ();
        Block[][] matrix = Game.getInstance ().getBlockMatrix ();
        if (checkLanded (matrix[x][y + 1])) {
            Game.getInstance ().nextBlock(r);
            return;
        }


        setY (getY () + 1);
    }

    private boolean checkLanded(Block blockBelow) {

        //if it can merge into another block
        float[] currentHSV = new float[3];
        Color.colorToHSV (color, currentHSV);//here is the possible inaccurate method
        if (blockBelow != null &&
                blockBelow.getColor () == color &&
                currentHSV[2] != colorValuesMap.get (4)) {
            Color.colorToHSV (blockBelow.getColor (), currentHSV);
            currentHSV[2] -= 0.2F;
            blockBelow.color = Color.HSVToColor (currentHSV); //here is the possible inaccurate method
            selfDestroy ();
            timer.stopTimer ();

            //if the block below is not the block at the bottom
            if (blockBelow.getY () != Game.MATRIX_HEIGHT - 1) {
                blockBelow.checkLanded (Game.getInstance ().
                        getBlockMatrix ()[blockBelow.getX ()][blockBelow.getY () + 1]);
            }

            return true;
        }

        //if a block is touched
        if (blockBelow != null &&
                blockBelow.getColor () != color) {
            timer.stopTimer ();
            return true;
        }

        return false;
    }

    private void selfDestroy () {
        Game.getInstance ().getBlockMatrix ()[x][y] = null;
        BlockView.updateViews ();
    }

    private boolean checkPositionValid (int x, int y) {
        return !(x < 0 || x >= Game.MATRIX_WIDTH ||
                y < 0 || y >= Game.MATRIX_HEIGHT)
                && Game.getInstance ().getBlockMatrix ()[x][y] == null;

    }

    public void moveHorizontally (@Direction int direction) {
        if (checkPositionValid (x + direction, y)) {
            setX (x + direction);
        }
    }
}

有人可以告诉我Color.colorToHSVColor.HSVToColor是不准确的还是我做错了什么?

只是为了通知你,如果有帮助,Game.getInstance().getHueNumber()会返回167

1 个答案:

答案 0 :(得分:0)

过了一段时间,我找到了解决方案。根据@Cinnam的评论,我知道HSV颜色使用float s并且它们是不准确的。 RGB对我来说不是一个好主意,因为我不能轻易知道色调。

根据Steve McConnell的 Code Complete 2nd Edition ,第12.3节:

  

......一种有效的方法是确定这些值是否足够接近。通常,您编写一个equals()函数,如果值足够接近则返回true,否则返回false。

我认为两种略有不同的颜色“大致相等”所以我写了一个这样的方法:

private boolean intColorEquals(int color1, int color2) {
    final int MAXIMUM_ERROR = 1000;
    return Math.abs (color1 - color2) < MAXIMUM_ERROR;
}

private boolean floatEquals (float a, float b) {
    final float MAXIMUM_ERROR = 0.05F;
    return Math.abs (a - b) < MAXIMUM_ERROR;
}

我替换了颜色之间的所有比较和浮动之间的比较。

我真的不知道我自己的问题的答案是否应该是这样的风格。所以,如果我做错了什么,请告诉我。