如何根据旋转偏移边界圆?

Posted

技术标签:

【中文标题】如何根据旋转偏移边界圆?【英文标题】:How can I offset a Bounding Circle based on a rotation? 【发布时间】:2021-10-26 18:44:11 【问题描述】:

我正在创建一个子弹射弹并给它一个绑定圈。我试图将圆圈设置在子弹的前面,这样当它撞击它时就会发生碰撞。下面是项目符号和显示边界圆位置的蓝色圆圈。

右下角的例子是我想要做的,但有任何旋转。关于如何动态偏移圆的任何想法?

HitBox.java(这是蓝色圆圈)

package com.icyrelic.game.object;

import com.icyrelic.game.object.entity.Entity;
import com.jogamp.opengl.math.VectorUtil;
import lombok.Getter;
import lombok.Setter;


public class HitBox 

    @Getter
    private GameObject object;
    @Getter @Setter
    private float width = 0, height = 0, xOffset = 0f, yOffset = 0f, radius = 0;


    public HitBox(GameObject object, float width, float height) 
        this.object = object;
        this.width = width;
        this.height = height;
    

    public HitBox(GameObject entity, float radius) 
        this.object = object;
        this.radius = radius;
    


    public boolean intersects(HitBox b) 
        boolean collisionX = getMinX() <= b.getMaxX() && getMaxX() >= b.getMinX();
        boolean collisionY = getMinY() <= b.getMaxY() && getMaxY() >= b.getMinY();

        return collisionX && collisionY;
    

    public boolean willCollide(HitBox b, float x, float y) 
        //Circle vs Circle
        if(radius != 0 && b.getRadius() != 0)
            return circleCollide(x, y, b.getRadius(), b.getXOffset(), b.getYOffset());

        //Circle vs AABB
        if((radius != 0 && b.getRadius() == 0) || (radius == 0 && b.getRadius() != 0))
            return circleAABBCollusion(b, x, y);

        //AABB vs AABB
        boolean collisionX = getMinX() <= x+b.getWidth()/2 && getMaxX() >= x-b.getWidth()/2;
        boolean collisionY = getMinY() <= y+b.getHeight()/2 && getMaxY() >= y-b.getHeight()/2;

        return collisionX && collisionY;
    

    private boolean circleCollide(float x2, float y2, float radius2, float xOffset2, float yOffset2) 
        double xDif = (object.getLocation().getX() + xOffset) - (x2 + xOffset2);
        double yDif = (object.getLocation().getY() + yOffset) - (y2 + yOffset2);
        double distanceSquared = xDif * xDif + yDif * yDif;

       return distanceSquared < (radius + radius2) * (radius + radius2);
    

    private boolean circleAABBCollusion(HitBox b, float x, float y) 
        HitBox circle = radius == 0 ? b : this;
        HitBox aabb = radius == 0 ? this : b;

        float[] circleCenter = radius == 0 ? new float[] x, y : new float[] object.getLocation().getX(), object.getLocation().getY();
        float[] aabbCenter = radius == 0 ? new float[] object.getLocation().getX(), object.getLocation().getY() : new float[] x, y;

        float closestX = circleCenter[0] + circle.getXOffset();
        float closestY = circleCenter[1] + circle.getYOffset();

        if(closestX < aabb.getMinX(aabbCenter[0])) closestX = aabb.getMinX(aabbCenter[0]);
        else if (closestX > aabb.getMaxX(aabbCenter[0])) closestX = aabb.getMaxX(aabbCenter[0]);

        if(closestY < aabb.getMinY(aabbCenter[1])) closestY = aabb.getMinY(aabbCenter[1]);
        else if (closestY > aabb.getMaxY(aabbCenter[1])) closestY = aabb.getMaxY(aabbCenter[1]);

        float dist = VectorUtil.distSquareVec3(new float[]circleCenter[0]+circle.getXOffset(), circleCenter[1]+circle.getYOffset(), 1, new float[]closestX, closestY, 1);

        return dist <= circle.getRadius() * circle.getRadius();
    

    private float getMinX()  return object.getLocation().getX()+xOffset - width/2; 
    private float getMaxX()  return object.getLocation().getX()+xOffset + width/2; 
    private float getMinY()  return object.getLocation().getY()+yOffset - height/2; 
    private float getMaxY()  return object.getLocation().getY()+yOffset + height/2;

    private float getMinX(float x)  return x+xOffset - width/2; 
    private float getMaxX(float x)  return x+xOffset + width/2; 
    private float getMinY(float y)  return y+yOffset - height/2; 
    private float getMaxY(float y)  return y+yOffset + height/2;

    public void setSize(float width, float height)  this.width = width; this.height = height; 
    public void setOffset(float x, float y)  this.xOffset = x; this.yOffset = y; 



Bullet.java

package com.icyrelic.game.object.projectile.type;

import com.icyrelic.game.Graphics.Animation;
import com.icyrelic.game.Graphics.ImageResource;
import com.icyrelic.game.object.projectile.Projectile;


public class Bullet extends Projectile 

    public Bullet(float x, float y, float width, float height, float rotation, float rotationOffset) 
        super(x,y,width, height, rotation, rotationOffset);
        hitBox.setRadius(0.03f);
        hitBox.setYOffset(-0.12f);
        location.setRotationOffset(-0f);



        animations = new Animation[1];
        ImageResource[] a = new ImageResource[1];
        a[0] = new ImageResource("/Projectile/Bullet.png");

        animations[0] = new Animation(a, 1);
    

    @Override
    public void update() 
        //location.setRotation(0);
    




Projectile.java

package com.icyrelic.game.object.projectile;

import com.icyrelic.game.Graphics.Graphics;
import com.icyrelic.game.object.GameObject;
import com.icyrelic.game.object.HitBox;
import lombok.Getter;


@Getter
public abstract class Projectile extends GameObject 

    protected float speed = 1.0f;
    protected HitBox hitBox;

    public Projectile(float x, float y, float width, float height, float rotation, float rotationOffset) 
        super(x, y, width, height, rotation, rotationOffset);
        this.hitBox = new HitBox(this, width, height);
    

    @Override
    public void render() 
        super.render();
        Graphics.setColor(0,0, 1, 1);

        if(hitBox.getRadius() == 0) 
            Graphics.drawRect(location.getX() + hitBox.getXOffset(), location.getY() + hitBox.getYOffset(), hitBox.getWidth(), hitBox.getHeight(), false);
         else 
            Graphics.drawHollowCircle(location.getX() + hitBox.getXOffset(), location.getY() + hitBox.getYOffset(), hitBox.getRadius());
        
        Graphics.setColor(1,0, 0, 1);
        //Graphics.drawRect(location.getX(), location.getY(), width/2, height/2, false);
        Graphics.setColor(1,1, 1, 1);

        Graphics.setColor(1,1, 1, 1);
    

GameObject.java

package com.icyrelic.game.object;

import com.icyrelic.game.Graphics.Animation;
import com.icyrelic.game.Graphics.Graphics;
import com.icyrelic.game.world.Location;
import lombok.Getter;



@Getter
public abstract class GameObject 


    protected Location location;

    protected float width = 1, height = 1;
    protected float xImageOffset = 0, yImageOffset = 0;

    protected Animation[] animations;
    protected int currentAnimation = 0;


    public GameObject(float x, float y, float width, float height, float rotation, float rotationOffset) 
        this.location = new Location(x, y, rotation, rotationOffset);
        this.width = width;
        this.height = height;


    

    public void render () 
        animations[currentAnimation].play();

        Graphics.setRotation(location.getRotation() + location.getRotationOffset());
        Graphics.drawImage(animations[currentAnimation].getImage(), location.getX(), location.getY(), xImageOffset, yImageOffset, width, height);
        Graphics.setRotation(0);

        //Graphics.setRotation(-rotation);





    

    public abstract void update ();

尝试翻译 Alberto 提供的信息后,我得到以下结果。它在正确的方向上有点接近,但仍然不是 100%

        hitBox.setXOffset(((float) Math.sin(location.getRotationRadians()) ));
        hitBox.setYOffset(((float) Math.cos(location.getRotationRadians()) ));

【问题讨论】:

可能是分享您的代码的一个很好的起点? 我添加了 Hitbox 和 Bullet 代码。不确定该问题究竟要分享什么,所以我希望我分享的内容就足够了。 可能还需要Projectile 添加,还添加了继承自弹丸的GameObject.java 你在哪里画蓝色圆圈? 【参考方案1】:

自从

Graphics.drawHollowCircle(location.getX() + hitBox.getXOffset(), location.getY() + hitBox.getYOffset(), hitBox.getRadius());

总是在底部画,这个

Graphics.drawHollowCircle(location.getX() + hitBox.getXOffset(), location.getY() + hitBox.getYOffset() / 2, hitBox.getRadius());

应该在中心画圆...然后使用一些三角函数来获得圆的中心,您必须执行以下操作:

float offsetX = Math.sin(location.getRotation()) * (hitBox.getYOffset() / 2);
float offsetY = Math.cos(location.getRotation()) * (hitBox.getYOffset() / 2);

Graphics.drawHollowCircle(location.getX() + offsetX, location.getY()  + offsetY, hitBox.getRadius());

假设location.getRotation() 将返回辐射点进行旋转

【讨论】:

所以 hitbox.getxoffset 和 getyoffset 默认为 0。 location.getX 和 location.getY 已经知道圆的中心,所以使用它只是辐射旋转的 sin/cos? 我添加了一张图片,其中包含我目前使用旋转的 cos 和 sin 得到的图像 @IcyRelic 返回什么location.getRotation()?我对Graphics.drawHollowCircle(location.getX() + hitBox.getXOffset(), location.getY() + hitBox.getYOffset() / 2, hitBox.getRadius());的目标是让蓝色圆圈画在图像的中心,你能确认一下吗?但是,坐标系统会根据渲染系统进行更改,因此这可能无法按预期工作,因此请确认使用该线(并且没有 sin/cos)蓝色圆圈位于所有图像的中心 如果我移除 yOffset 以在底部绘制它,它已经在中心绘制。 x 和 y 是蓝色圆圈的中心。因此,您提供的偏移量只会撤消我放置的偏移量。我能够将圆置于几乎正确的位置,将 x 上的旋转罪数乘以 0.12f,y 上的旋转 cos 乘以 -0.12,其中一些取决于旋转,正好在它们应该在的位置和其他旋转有点偏离。 @IcyRelic 请立即尝试...如果不起作用,请使用Graphics.drawHollowCircle(location.getX(), location.getY(), hitBox.getRadius());发布图片

以上是关于如何根据旋转偏移边界圆?的主要内容,如果未能解决你的问题,请参考以下文章

计算给定球体上扭曲椭圆的偏移量

Python习题:给定一个字符串和一个偏移量,根据偏移量旋转字符串(从左向右旋转)。例:输入: str="abcdefg", offset = 3 输出: "efgab

如何沿固定路径拖动项目? [在 Qt 图形视图中]

如何使相机具有与对象相同的旋转但具有偏移(Unity)

偏移图像框内的平铺形状

Unity相机跟随-----根据速度设置偏移量