如果用户点击圆圈附近,将圆圈按钮移动到随机位置的应用程序
App that moves circle button to random location if user clicks near circle
我目前有一个 Android 应用程序,如果我点击屏幕上 不是 圆圈的某个位置,它会将绘制的圆圈移动到屏幕上的随机位置本身。
我的问题是,我怎样才能实现它,以便仅在点击相对靠近圆圈时才执行此逻辑?距离有多近没有严格定义,但是,如果圆圈位于屏幕的左下角,则单击屏幕右上角不会触发圆圈移动到新位置。我想象的是一个比圆形稍大的正方形,它可以作为碰撞框(但仍然不包括圆圈内的点击)
我假设更改必须在 isInsideCircle 函数中发生,但我缺乏必要的知识来理解我应该 add/subtract 哪些变量来限制区域这触发了逻辑。
这是我目前拥有的代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Circle(this));
}
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructor
public Circle(Context context) {
super(context);
}
// gets random coordinates
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isInsideCircle(event.getX(), event.getY())) { // only register clicks outside of circle
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return ((dxPow + dyPow) <= radPow));
}
}
}
任何帮助将不胜感激,谢谢!
这个问题有几个部分,我想确保在讨论之前已经很清楚了。
- 我们希望将命中区域修改为正方形,而不是圆形
- 我们想颠倒我们的逻辑,所以我们不想在命中区域外部时做出反应,而是在命中区域内部时做出反应面积。
实际上我要先从第二点开始,然后再转到第一点。
测试内部,而不是外部
这个相当简单,因为您已经在测试点击是否在圆圈内,至少 Circle
[=111] 是这样假设的=]' isInsideCircle
方法。碰巧的是,可以相当安全地假设,如果您 不是 (!) 在圈内,那么您在圈外。将 if (!isInsideCircle(...))
修改为 if(isInsideCircle(...))
(删除 not [!]) 运算符)应该在这方面对我们有所帮助,不是吗?好吧,那是相当轻松的,希望测试在正方形内也一样容易!
测试正方形,而不是圆形
...不会像检查我们是否在命中区域内部而不是外部那样容易。
我的意思是它确实需要 isInsideCircle
执行的逻辑完全转变。另外,我不确定更改 isInsideCircle
的正文以测试它是否在 正方形 内是否是个好主意。这可能有点令人困惑,不是吗?
因此,制作一种新方法来测试给定的 (x, y) 坐标是否在正方形内可能是一个更好的主意。也许 isInsideSquare
?等一会儿! Square
肯定不是 Circle
那么,我们需要什么信息来表示 Square
?那么现在我们需要一种方法来表示 Square
和一种判断坐标是否在 内 正方形的方法。正方形是一种特殊的矩形,您实际上只需要一个特殊的坐标位置(一个顶点或精确的中心)和一个边长来比较坐标。我们恰好有一个可以用作中心的坐标位置,所以我想我们需要一个碰撞框的边长。让我们在我们的圆 class 中添加一个新字段来表示碰撞框的边长。以下行将默认它比圆的直径多 50 像素。
private int hitboxLength = (2 * r) + 50;
现在我们有了一个坐标点,不幸的是,它位于正方形的 中间 和它的边长。那么,我们如何确定一个点是否在我们的方形碰撞箱内?很高兴知道矩形也可以描述为有 4 个顶点。对于以 (0, 0) 为中心的边长 2 的正方形,您将在 (1, 1)、(1, -1)、(-1, -1) 和 (-1, 1) 处拥有顶点。为了找出 (x, y) 是否在我们的矩形内,我们只需要它不在这 4 个坐标内。 X 的范围从 1 到 -1,Y 的范围也从 1 到 -1。意思是如果 x 在 -1 和 1 -1 <= x <= 1
之间并且 y 在 -1 和 1 -1 <= y <= 1
.
之间
boolean isInsideSquare(float xPoint, float yPoint) {
// The center of a square is HALF it's length from any vertex.
// Always try to use equivalent number types when comparing
float halfHitboxLength = this.hitboxLength / 2.0F;
float xUpperBound = this.x + halfHitboxLength;
float xLowerBound = this.x - halfHitboxLength;
// smallest expected X <= xPoint <= largest expected X
boolean xInSquare = xLowerBound <= xPoint && xPoint <= xUpperBound;
float yUpperBound = this.y + halfHitboxLength;
float yLowerBound = this.y - halfHitboxLength;
boolean yInSquare = yLowerBound <= yPoint && yPoint <= yUpperBound;
return xInSquare && yInSquare;
}
毫无疑问,这不是处理此问题的最有效方法,但它应该 为我们提供在给定方块内的逻辑。
现在我们只需要在 onTouchEvent
方法中使用 isInsideSquare
而不是 isInsideCircle
。
// This "if"
if (!isInsideCircle(event.getX(), event.getY())) {
// Would become
if (!isInsideSquare(event.getX(), event.getY())) {
但是,我不禁觉得用正方形来表示圆有点奇怪。至少,考虑到它们的形状有多么不同。更不用说 isInsideSquare
是否属于 Circle
class 的问题了!想要用另一个稍大的圆圈外观来表示我们圆圈的命中区域如何?
测试稍大的圆圈
我们已经有了检查 (x, y) 坐标对是否在我们当前圆圈内的逻辑。我的意思是有一种方法叫做 isInsideCircle
.
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return ((dxPow + dyPow) <= radPow));
}
就个人而言,我的注意力会立即被吸引到几个地方
- 利用少量变量的return
return ((dxPow + dyPow) <= radPow));
,
- 与用户输入无关的变量
float radPow = (float) Math.pow(r, 2);
return,因为这是我们确定给定的 (xPoint
、yPoint
) 对是否在我们的圆实例内的方式,但更重要的是,radPow
变量,因为它似乎是引用,我只能假设是圆的半径,并且要针对更大的圆进行测试,我们真正需要做的就是调整它的半径!
int userWithinPixels = 50;
float radPow = (float) Math.pow(r + userWithinPixels, 2);
嗯,这很轻松!
再次值得指出的是,在 Circle
对象上调用 isInsideCircle
可能会测试它是否在表示的圆圈内而不是稍大的圆圈内,但这仍然是个人偏好,您应该做对项目的程序员有意义的事情。
使用正方形
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Circle(this));
}
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
// Add hitbox side length
private int hitboxLength = (2 * r) + 50;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructor
public Circle(Context context) {
super(context);
}
// gets random coordinates
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isInsideSquare(event.getX(), event.getY())) { // only register clicks outside of circle
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideSquare(float xPoint, float yPoint) {
// The center of a square is HALF it's length from any vertex.
// Always try to use the same number types when comparing/computing
float halfHitboxLength = this.hitboxLength / 2.0F;
float xUpperBound = this.x + halfHitboxLength;
float xLowerBound = this.x - halfHitboxLength;
// smallest expected X <= xPoint <= largest expected X
boolean xInSquare = xLowerBound <= xPoint && xPoint <= xUpperBound;
float yUpperBound = this.y + halfHitboxLength;
float yLowerBound = this.y - halfHitboxLength;
boolean yInSquare = yLowerBound <= yPoint && yPoint <= yUpperBound;
return xInSquare && yInSquare;
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return ((dxPow + dyPow) <= radPow));
}
}
}
使用更大的圆圈
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Circle(this));
}
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructor
public Circle(Context context) {
super(context);
}
// gets random coordinates
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isInsideCircle(event.getX(), event.getY())) { // only register clicks outside of circle
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
// maximum amount of pixels user can click off the circle and trigger event
float userWithinPixels = 25;
float radPow = (float) Math.pow(r + userWithinPixels, 2);
return ((dxPow + dyPow) <= radPow));
}
}
}
我目前有一个 Android 应用程序,如果我点击屏幕上 不是 圆圈的某个位置,它会将绘制的圆圈移动到屏幕上的随机位置本身。
我的问题是,我怎样才能实现它,以便仅在点击相对靠近圆圈时才执行此逻辑?距离有多近没有严格定义,但是,如果圆圈位于屏幕的左下角,则单击屏幕右上角不会触发圆圈移动到新位置。我想象的是一个比圆形稍大的正方形,它可以作为碰撞框(但仍然不包括圆圈内的点击)
我假设更改必须在 isInsideCircle 函数中发生,但我缺乏必要的知识来理解我应该 add/subtract 哪些变量来限制区域这触发了逻辑。 这是我目前拥有的代码:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Circle(this));
}
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructor
public Circle(Context context) {
super(context);
}
// gets random coordinates
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isInsideCircle(event.getX(), event.getY())) { // only register clicks outside of circle
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return ((dxPow + dyPow) <= radPow));
}
}
}
任何帮助将不胜感激,谢谢!
这个问题有几个部分,我想确保在讨论之前已经很清楚了。
- 我们希望将命中区域修改为正方形,而不是圆形
- 我们想颠倒我们的逻辑,所以我们不想在命中区域外部时做出反应,而是在命中区域内部时做出反应面积。
实际上我要先从第二点开始,然后再转到第一点。
测试内部,而不是外部
这个相当简单,因为您已经在测试点击是否在圆圈内,至少 Circle
[=111] 是这样假设的=]' isInsideCircle
方法。碰巧的是,可以相当安全地假设,如果您 不是 (!) 在圈内,那么您在圈外。将 if (!isInsideCircle(...))
修改为 if(isInsideCircle(...))
(删除 not [!]) 运算符)应该在这方面对我们有所帮助,不是吗?好吧,那是相当轻松的,希望测试在正方形内也一样容易!
测试正方形,而不是圆形
...不会像检查我们是否在命中区域内部而不是外部那样容易。
我的意思是它确实需要 isInsideCircle
执行的逻辑完全转变。另外,我不确定更改 isInsideCircle
的正文以测试它是否在 正方形 内是否是个好主意。这可能有点令人困惑,不是吗?
因此,制作一种新方法来测试给定的 (x, y) 坐标是否在正方形内可能是一个更好的主意。也许 isInsideSquare
?等一会儿! Square
肯定不是 Circle
那么,我们需要什么信息来表示 Square
?那么现在我们需要一种方法来表示 Square
和一种判断坐标是否在 内 正方形的方法。正方形是一种特殊的矩形,您实际上只需要一个特殊的坐标位置(一个顶点或精确的中心)和一个边长来比较坐标。我们恰好有一个可以用作中心的坐标位置,所以我想我们需要一个碰撞框的边长。让我们在我们的圆 class 中添加一个新字段来表示碰撞框的边长。以下行将默认它比圆的直径多 50 像素。
private int hitboxLength = (2 * r) + 50;
现在我们有了一个坐标点,不幸的是,它位于正方形的 中间 和它的边长。那么,我们如何确定一个点是否在我们的方形碰撞箱内?很高兴知道矩形也可以描述为有 4 个顶点。对于以 (0, 0) 为中心的边长 2 的正方形,您将在 (1, 1)、(1, -1)、(-1, -1) 和 (-1, 1) 处拥有顶点。为了找出 (x, y) 是否在我们的矩形内,我们只需要它不在这 4 个坐标内。 X 的范围从 1 到 -1,Y 的范围也从 1 到 -1。意思是如果 x 在 -1 和 1 -1 <= x <= 1
之间并且 y 在 -1 和 1 -1 <= y <= 1
.
boolean isInsideSquare(float xPoint, float yPoint) {
// The center of a square is HALF it's length from any vertex.
// Always try to use equivalent number types when comparing
float halfHitboxLength = this.hitboxLength / 2.0F;
float xUpperBound = this.x + halfHitboxLength;
float xLowerBound = this.x - halfHitboxLength;
// smallest expected X <= xPoint <= largest expected X
boolean xInSquare = xLowerBound <= xPoint && xPoint <= xUpperBound;
float yUpperBound = this.y + halfHitboxLength;
float yLowerBound = this.y - halfHitboxLength;
boolean yInSquare = yLowerBound <= yPoint && yPoint <= yUpperBound;
return xInSquare && yInSquare;
}
毫无疑问,这不是处理此问题的最有效方法,但它应该 为我们提供在给定方块内的逻辑。
现在我们只需要在 onTouchEvent
方法中使用 isInsideSquare
而不是 isInsideCircle
。
// This "if"
if (!isInsideCircle(event.getX(), event.getY())) {
// Would become
if (!isInsideSquare(event.getX(), event.getY())) {
但是,我不禁觉得用正方形来表示圆有点奇怪。至少,考虑到它们的形状有多么不同。更不用说 isInsideSquare
是否属于 Circle
class 的问题了!想要用另一个稍大的圆圈外观来表示我们圆圈的命中区域如何?
测试稍大的圆圈
我们已经有了检查 (x, y) 坐标对是否在我们当前圆圈内的逻辑。我的意思是有一种方法叫做 isInsideCircle
.
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return ((dxPow + dyPow) <= radPow));
}
就个人而言,我的注意力会立即被吸引到几个地方
- 利用少量变量的return
return ((dxPow + dyPow) <= radPow));
, - 与用户输入无关的变量
float radPow = (float) Math.pow(r, 2);
return,因为这是我们确定给定的 (xPoint
、yPoint
) 对是否在我们的圆实例内的方式,但更重要的是,radPow
变量,因为它似乎是引用,我只能假设是圆的半径,并且要针对更大的圆进行测试,我们真正需要做的就是调整它的半径!
int userWithinPixels = 50;
float radPow = (float) Math.pow(r + userWithinPixels, 2);
嗯,这很轻松!
再次值得指出的是,在 Circle
对象上调用 isInsideCircle
可能会测试它是否在表示的圆圈内而不是稍大的圆圈内,但这仍然是个人偏好,您应该做对项目的程序员有意义的事情。
使用正方形
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Circle(this));
}
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
// Add hitbox side length
private int hitboxLength = (2 * r) + 50;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructor
public Circle(Context context) {
super(context);
}
// gets random coordinates
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isInsideSquare(event.getX(), event.getY())) { // only register clicks outside of circle
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideSquare(float xPoint, float yPoint) {
// The center of a square is HALF it's length from any vertex.
// Always try to use the same number types when comparing/computing
float halfHitboxLength = this.hitboxLength / 2.0F;
float xUpperBound = this.x + halfHitboxLength;
float xLowerBound = this.x - halfHitboxLength;
// smallest expected X <= xPoint <= largest expected X
boolean xInSquare = xLowerBound <= xPoint && xPoint <= xUpperBound;
float yUpperBound = this.y + halfHitboxLength;
float yLowerBound = this.y - halfHitboxLength;
boolean yInSquare = yLowerBound <= yPoint && yPoint <= yUpperBound;
return xInSquare && yInSquare;
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
float radPow = (float) Math.pow(r, 2);
return ((dxPow + dyPow) <= radPow));
}
}
}
使用更大的圆圈
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Circle(this));
}
public class Circle extends View {
private float x = 300;
private float y = 300;
private int r = 150;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Random random = new Random();
// draws circle
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
canvas.drawCircle(x, y, r, mPaint);
}
// constructor
public Circle(Context context) {
super(context);
}
// gets random coordinates
void generateRandom() {
int w = getWidth() - r - 50;
int h = getHeight()- r - 50;
int border = r + 50;
this.x = border + random.nextInt(w-border);
this.y = border + random.nextInt(h-border);
}
// when screen is tapped, old circle removed, new circle drawn
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isInsideCircle(event.getX(), event.getY())) { // only register clicks outside of circle
generateRandom();
invalidate();
}
return super.onTouchEvent(event);
}
boolean isInsideCircle(float xPoint, float yPoint) {
float dx = (x - xPoint);
float dxPow = (float) Math.pow(dx, 2);
float dy = (y - yPoint);
float dyPow = (float) Math.pow(dy, 2);
// maximum amount of pixels user can click off the circle and trigger event
float userWithinPixels = 25;
float radPow = (float) Math.pow(r + userWithinPixels, 2);
return ((dxPow + dyPow) <= radPow));
}
}
}