操纵杆死区计算

发布于 2024-08-15 11:00:52 字数 7395 浏览 6 评论 0原文

我的问题:给定 x 和 y,我需要计算所需操纵杆偏转的 x 和 y。

当没有操纵杆死区时,这很简单——我只使用 x 和 y 而不进行任何操作。

当存在死区时,我希望 x=0 为零,x=非零为该方向上死区之外的第一个值。

方形死区很简单。在下面的代码中,x 和 y 的范围是从 -1 到 1(包括 -1 和 1)。死区是从 0 到 1(含)。

float xDeflection = 0;
if (x > 0)
 xDeflection = (1 - deadzone) * x + deadzone;
else if (x < 0)
 xDeflection = (1 - deadzone) * x - deadzone;

float yDeflection = 0;
if (y > 0)
 yDeflection = (1 - deadzone) * y + deadzone;
else if (y < 0)
 yDeflection = (1 - deadzone) * y - deadzone;

圆形死区更加棘手。经过一番闲逛后,我想出了这个:

float xDeflection = 0, yDeflection = 0;
if (x != 0 || y != 0) {
 float distRange = 1 - deadzone;
 float dist = distRange * (float)Math.sqrt(x * x + y * y) + deadzone;
 double angle = Math.atan2(x, y);
 xDeflection = dist * (float)Math.sin(angle);
 yDeflection = dist * (float)Math.cos(angle);
}

这是极端情况下操纵杆偏转的输出(deadzone=0.25):

非方形操纵杆偏转。 http://n4te.com/temp/nonsquare.gif

正如您所看到的,偏转没有延伸到角落。即,如果 x=1,y=1,则 xDeflection 和 yDeflection 都等于 0.918。随着死区变大,问题会变得更加严重,使得上图中的绿线看起来越来越像一个圆圈。当 deadzone=1 时,绿线是与死区匹配的圆圈。

我发现,通过一个小小的改变,我可以放大绿线代表的形状,并在 -1 到 1 之外裁剪值:

if (x != 0 || y != 0) {
 float distRange = 1 - 0.71f * deadzone;
 float dist = distRange * (float)Math.sqrt(x * x + y * y) + deadzone;
 double angle = Math.atan2(x, y);
 xDeflection = dist * (float)Math.sin(angle);
 xDeflection = Math.min(1, Math.max(-1, xDeflection));
 yDeflection = dist * (float)Math.cos(angle);
 yDeflection = Math.min(1, Math.max(-1, yDeflection));
}

我通过反复试验得出了常数 0.71。该数字使形状足够大,使得角点与实际角点的距离在小数点后几位以内。出于学术原因,谁能解释为什么 0.71 恰好是执行此操作的数字?

总的来说,我不太确定我是否采取了正确的方法。有没有更好的方法来实现我对圆形死区的需求?

我编写了一个简单的基于 Swing 的程序来直观地了解正在发生的情况:

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Hashtable;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DeadzoneTest extends JFrame {
 float xState, yState;
 float deadzone = 0.3f;
 int size = (int)(255 * deadzone);

 public DeadzoneTest () {
  super("DeadzoneTest");
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);

  final CardLayout cardLayout = new CardLayout();
  final JPanel centerPanel = new JPanel(cardLayout);
  getContentPane().add(centerPanel, BorderLayout.CENTER);
  centerPanel.setPreferredSize(new Dimension(512, 512));

  Hashtable labels = new Hashtable();
  labels.put(-255, new JLabel("-1"));
  labels.put(-128, new JLabel("-0.5"));
  labels.put(0, new JLabel("0"));
  labels.put(128, new JLabel("0.5"));
  labels.put(255, new JLabel("1"));

  final JSlider ySlider = new JSlider(JSlider.VERTICAL, -256, 256, 0);
  getContentPane().add(ySlider, BorderLayout.EAST);
  ySlider.setInverted(true);
  ySlider.setLabelTable(labels);
  ySlider.setPaintLabels(true);
  ySlider.setMajorTickSpacing(32);
  ySlider.setSnapToTicks(true);
  ySlider.addChangeListener(new ChangeListener() {
   public void stateChanged (ChangeEvent event) {
    yState = ySlider.getValue() / 255f;
    centerPanel.repaint();
   }
  });

  final JSlider xSlider = new JSlider(JSlider.HORIZONTAL, -256, 256, 0);
  getContentPane().add(xSlider, BorderLayout.SOUTH);
  xSlider.setLabelTable(labels);
  xSlider.setPaintLabels(true);
  xSlider.setMajorTickSpacing(32);
  xSlider.setSnapToTicks(true);
  xSlider.addChangeListener(new ChangeListener() {
   public void stateChanged (ChangeEvent event) {
    xState = xSlider.getValue() / 255f;
    centerPanel.repaint();
   }
  });

  final JSlider deadzoneSlider = new JSlider(JSlider.VERTICAL, 0, 100, 33);
  getContentPane().add(deadzoneSlider, BorderLayout.WEST);
  deadzoneSlider.setInverted(true);
  deadzoneSlider.createStandardLabels(25);
  deadzoneSlider.setPaintLabels(true);
  deadzoneSlider.setMajorTickSpacing(25);
  deadzoneSlider.setSnapToTicks(true);
  deadzoneSlider.addChangeListener(new ChangeListener() {
   public void stateChanged (ChangeEvent event) {
    deadzone = deadzoneSlider.getValue() / 100f;
    size = (int)(255 * deadzone);
    centerPanel.repaint();
   }
  });

  final JComboBox combo = new JComboBox();
  combo.setModel(new DefaultComboBoxModel(new Object[] {"round", "square"}));
  getContentPane().add(combo, BorderLayout.NORTH);
  combo.addActionListener(new ActionListener() {
   public void actionPerformed (ActionEvent event) {
    cardLayout.show(centerPanel, (String)combo.getSelectedItem());
   }
  });

  centerPanel.add(new Panel() {
   public void toDeflection (Graphics g, float x, float y) {
    g.drawRect(256 - size, 256 - size, size * 2, size * 2);
    float xDeflection = 0;
    if (x > 0)
     xDeflection = (1 - deadzone) * x + deadzone;
    else if (x < 0) {
     xDeflection = (1 - deadzone) * x - deadzone;
    }
    float yDeflection = 0;
    if (y > 0)
     yDeflection = (1 - deadzone) * y + deadzone;
    else if (y < 0) {
     yDeflection = (1 - deadzone) * y - deadzone;
    }
    draw(g, xDeflection, yDeflection);
   }
  }, "square");

  centerPanel.add(new Panel() {
   public void toDeflection (Graphics g, float x, float y) {
    g.drawOval(256 - size, 256 - size, size * 2, size * 2);
    float xDeflection = 0, yDeflection = 0;
    if (x != 0 || y != 0) {
     float distRange = 1 - 0.71f * deadzone;
     float dist = distRange * (float)Math.sqrt(x * x + y * y) + deadzone;
     double angle = Math.atan2(x, y);
     xDeflection = dist * (float)Math.sin(angle);
     xDeflection = Math.min(1, Math.max(-1, xDeflection));
     yDeflection = dist * (float)Math.cos(angle);
     yDeflection = Math.min(1, Math.max(-1, yDeflection));
    }
    draw(g, xDeflection, yDeflection);
   }
  }, "round");

  cardLayout.show(centerPanel, (String)combo.getSelectedItem());
  pack();
  setLocationRelativeTo(null);
  setVisible(true);
 }

 private abstract class Panel extends JPanel {
  public void paintComponent (Graphics g) {
   g.setColor(Color.gray);
   g.fillRect(0, 0, getWidth(), getHeight());
   g.setColor(Color.white);
   g.fillRect(0, 0, 512, 512);

   g.setColor(Color.green);
   if (true) {
    // Draws all edge points.
    for (int i = -255; i < 256; i++)
     toDeflection(g, i / 255f, 1);
    for (int i = -255; i < 256; i++)
     toDeflection(g, i / 255f, -1);
    for (int i = -255; i < 256; i++)
     toDeflection(g, 1, i / 255f);
    for (int i = -255; i < 256; i++)
     toDeflection(g, -1, i / 255f);
   } else if (false) {
    // Draws all possible points (slow).
    for (int x = -255; x < 256; x++)
     for (int y = -255; y < 256; y++)
      toDeflection(g, x / 255f, y / 255f);
   }

   g.setColor(Color.red);
   toDeflection(g, xState, yState);
  }

  abstract public void toDeflection (Graphics g, float x, float y);

  public void draw (Graphics g, float xDeflection, float yDeflection) {
   int r = 5, d = r * 2;
   g.fillRect((int)(xDeflection * 256) + 256 - r, (int)(yDeflection * 256) + 256 - r, d, d);
  }
 }

 public static void main (String[] args) {
  new DeadzoneTest();
 }
}

My problem: given x and y, I need to calculate the x and y for the required joystick deflection.

This is simple when there is no joystick deadzone -- I just use the x and y with no manipulation.

When there is a deadzone, I want x=0 to be zero and x=non-zero to be the first value in that direction that is outside the deadzone.

A square deadzone is simple. In the following code x and y are from -1 to 1 inclusive. The deadzone is from 0 to 1 inclusive.

float xDeflection = 0;
if (x > 0)
 xDeflection = (1 - deadzone) * x + deadzone;
else if (x < 0)
 xDeflection = (1 - deadzone) * x - deadzone;

float yDeflection = 0;
if (y > 0)
 yDeflection = (1 - deadzone) * y + deadzone;
else if (y < 0)
 yDeflection = (1 - deadzone) * y - deadzone;

A circular deadzone is trickier. After a whole lot of fooling around I came up with this:

float xDeflection = 0, yDeflection = 0;
if (x != 0 || y != 0) {
 float distRange = 1 - deadzone;
 float dist = distRange * (float)Math.sqrt(x * x + y * y) + deadzone;
 double angle = Math.atan2(x, y);
 xDeflection = dist * (float)Math.sin(angle);
 yDeflection = dist * (float)Math.cos(angle);
}

Here is what this outputs for the joystick deflection at the extremes (deadzone=0.25):

Non-square joystick deflection. http://n4te.com/temp/nonsquare.gif

As you can see, the deflection does not extend to the corners. IE, if x=1,y=1 then the xDeflection and yDeflection both equal something like 0.918. The problem worsens with larger deadzones, making the green lines in the image above look more and more like a circle. At deadzone=1 the green lines are a circle that matches the deadzone.

I found that with a small change I could enlarge the shape represented by the green lines and clip values outside of -1 to 1:

if (x != 0 || y != 0) {
 float distRange = 1 - 0.71f * deadzone;
 float dist = distRange * (float)Math.sqrt(x * x + y * y) + deadzone;
 double angle = Math.atan2(x, y);
 xDeflection = dist * (float)Math.sin(angle);
 xDeflection = Math.min(1, Math.max(-1, xDeflection));
 yDeflection = dist * (float)Math.cos(angle);
 yDeflection = Math.min(1, Math.max(-1, yDeflection));
}

I came up with the constant 0.71 from trial and error. This number makes the shape large enough that the corners are within a few decimal places of the actual corners. For academic reasons, can anyone explain why 0.71 happens to be the number that does this?

Overall, I'm not really sure if I am taking the right approach. Is there a better way to accomplish what I need for a circular deadzone?

I have written a simple Swing-based program to visual what is going on:

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Hashtable;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DeadzoneTest extends JFrame {
 float xState, yState;
 float deadzone = 0.3f;
 int size = (int)(255 * deadzone);

 public DeadzoneTest () {
  super("DeadzoneTest");
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);

  final CardLayout cardLayout = new CardLayout();
  final JPanel centerPanel = new JPanel(cardLayout);
  getContentPane().add(centerPanel, BorderLayout.CENTER);
  centerPanel.setPreferredSize(new Dimension(512, 512));

  Hashtable labels = new Hashtable();
  labels.put(-255, new JLabel("-1"));
  labels.put(-128, new JLabel("-0.5"));
  labels.put(0, new JLabel("0"));
  labels.put(128, new JLabel("0.5"));
  labels.put(255, new JLabel("1"));

  final JSlider ySlider = new JSlider(JSlider.VERTICAL, -256, 256, 0);
  getContentPane().add(ySlider, BorderLayout.EAST);
  ySlider.setInverted(true);
  ySlider.setLabelTable(labels);
  ySlider.setPaintLabels(true);
  ySlider.setMajorTickSpacing(32);
  ySlider.setSnapToTicks(true);
  ySlider.addChangeListener(new ChangeListener() {
   public void stateChanged (ChangeEvent event) {
    yState = ySlider.getValue() / 255f;
    centerPanel.repaint();
   }
  });

  final JSlider xSlider = new JSlider(JSlider.HORIZONTAL, -256, 256, 0);
  getContentPane().add(xSlider, BorderLayout.SOUTH);
  xSlider.setLabelTable(labels);
  xSlider.setPaintLabels(true);
  xSlider.setMajorTickSpacing(32);
  xSlider.setSnapToTicks(true);
  xSlider.addChangeListener(new ChangeListener() {
   public void stateChanged (ChangeEvent event) {
    xState = xSlider.getValue() / 255f;
    centerPanel.repaint();
   }
  });

  final JSlider deadzoneSlider = new JSlider(JSlider.VERTICAL, 0, 100, 33);
  getContentPane().add(deadzoneSlider, BorderLayout.WEST);
  deadzoneSlider.setInverted(true);
  deadzoneSlider.createStandardLabels(25);
  deadzoneSlider.setPaintLabels(true);
  deadzoneSlider.setMajorTickSpacing(25);
  deadzoneSlider.setSnapToTicks(true);
  deadzoneSlider.addChangeListener(new ChangeListener() {
   public void stateChanged (ChangeEvent event) {
    deadzone = deadzoneSlider.getValue() / 100f;
    size = (int)(255 * deadzone);
    centerPanel.repaint();
   }
  });

  final JComboBox combo = new JComboBox();
  combo.setModel(new DefaultComboBoxModel(new Object[] {"round", "square"}));
  getContentPane().add(combo, BorderLayout.NORTH);
  combo.addActionListener(new ActionListener() {
   public void actionPerformed (ActionEvent event) {
    cardLayout.show(centerPanel, (String)combo.getSelectedItem());
   }
  });

  centerPanel.add(new Panel() {
   public void toDeflection (Graphics g, float x, float y) {
    g.drawRect(256 - size, 256 - size, size * 2, size * 2);
    float xDeflection = 0;
    if (x > 0)
     xDeflection = (1 - deadzone) * x + deadzone;
    else if (x < 0) {
     xDeflection = (1 - deadzone) * x - deadzone;
    }
    float yDeflection = 0;
    if (y > 0)
     yDeflection = (1 - deadzone) * y + deadzone;
    else if (y < 0) {
     yDeflection = (1 - deadzone) * y - deadzone;
    }
    draw(g, xDeflection, yDeflection);
   }
  }, "square");

  centerPanel.add(new Panel() {
   public void toDeflection (Graphics g, float x, float y) {
    g.drawOval(256 - size, 256 - size, size * 2, size * 2);
    float xDeflection = 0, yDeflection = 0;
    if (x != 0 || y != 0) {
     float distRange = 1 - 0.71f * deadzone;
     float dist = distRange * (float)Math.sqrt(x * x + y * y) + deadzone;
     double angle = Math.atan2(x, y);
     xDeflection = dist * (float)Math.sin(angle);
     xDeflection = Math.min(1, Math.max(-1, xDeflection));
     yDeflection = dist * (float)Math.cos(angle);
     yDeflection = Math.min(1, Math.max(-1, yDeflection));
    }
    draw(g, xDeflection, yDeflection);
   }
  }, "round");

  cardLayout.show(centerPanel, (String)combo.getSelectedItem());
  pack();
  setLocationRelativeTo(null);
  setVisible(true);
 }

 private abstract class Panel extends JPanel {
  public void paintComponent (Graphics g) {
   g.setColor(Color.gray);
   g.fillRect(0, 0, getWidth(), getHeight());
   g.setColor(Color.white);
   g.fillRect(0, 0, 512, 512);

   g.setColor(Color.green);
   if (true) {
    // Draws all edge points.
    for (int i = -255; i < 256; i++)
     toDeflection(g, i / 255f, 1);
    for (int i = -255; i < 256; i++)
     toDeflection(g, i / 255f, -1);
    for (int i = -255; i < 256; i++)
     toDeflection(g, 1, i / 255f);
    for (int i = -255; i < 256; i++)
     toDeflection(g, -1, i / 255f);
   } else if (false) {
    // Draws all possible points (slow).
    for (int x = -255; x < 256; x++)
     for (int y = -255; y < 256; y++)
      toDeflection(g, x / 255f, y / 255f);
   }

   g.setColor(Color.red);
   toDeflection(g, xState, yState);
  }

  abstract public void toDeflection (Graphics g, float x, float y);

  public void draw (Graphics g, float xDeflection, float yDeflection) {
   int r = 5, d = r * 2;
   g.fillRect((int)(xDeflection * 256) + 256 - r, (int)(yDeflection * 256) + 256 - r, d, d);
  }
 }

 public static void main (String[] args) {
  new DeadzoneTest();
 }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

囍笑 2024-08-22 11:00:52

如果您有一个圆形死区,则 0.71 实际上是 0.70710678 或 2 平方根的一半
利用毕达哥拉斯定理进行计算

If you have a circular deadzone the .71 is actually 0.70710678 or the half of the squareroot of 2
Calculation thanks to theorem of Pythagoras

感性不性感 2024-08-22 11:00:52

这是我拼凑起来的。它的行为有点奇怪,但在边界上它很好:

private Point2D.Float calculateDeflection(float x, float y) {
    Point2D.Float center = new Point2D.Float(0, 0);
    Point2D.Float joyPoint = new Point2D.Float(x, y);
    Double angleRad = Math.atan2(y, x);

    float maxDist = getMaxDist(joyPoint);

    float factor = (maxDist - deadzone) / maxDist;

    Point2D.Float factoredPoint = new Point2D.Float(x * factor, y * factor);

    float factoredDist = (float) center.distance(factoredPoint);

    float finalDist = factoredDist + deadzone;

    float finalX = finalDist * (float) Math.cos(angleRad);
    float finalY = finalDist * (float) Math.sin(angleRad);

    Point2D.Float finalPoint = new Point2D.Float(finalX, finalY);

    return finalPoint;
}

编辑:错过了这个。

private float getMaxDist(Point2D.Float point) {
    float xMax;
    float yMax;
    if (Math.abs(point.x) > Math.abs(point.y)) {
        xMax = Math.signum(point.x);
        yMax = point.y * point.x / xMax;
    } else {
        yMax = Math.signum(point.y);
        xMax = point.x * point.y / yMax;
    }
    Point2D.Float maxPoint = new Point2D.Float(xMax, yMax);
    Point2D.Float center = new Point2D.Float(0, 0);
    return (float) center.distance(maxPoint);
}

它保留角度,但将距离从 0 到边界之间的某个位置缩放到死区和边界之间。最大距离会有所不同,因为边上的距离为 1,角上的距离为 sqrt(2),因此必须相应地更改缩放比例。

This is what I threw together. It behaves a bit wierd, but in the boundaries it's good:

private Point2D.Float calculateDeflection(float x, float y) {
    Point2D.Float center = new Point2D.Float(0, 0);
    Point2D.Float joyPoint = new Point2D.Float(x, y);
    Double angleRad = Math.atan2(y, x);

    float maxDist = getMaxDist(joyPoint);

    float factor = (maxDist - deadzone) / maxDist;

    Point2D.Float factoredPoint = new Point2D.Float(x * factor, y * factor);

    float factoredDist = (float) center.distance(factoredPoint);

    float finalDist = factoredDist + deadzone;

    float finalX = finalDist * (float) Math.cos(angleRad);
    float finalY = finalDist * (float) Math.sin(angleRad);

    Point2D.Float finalPoint = new Point2D.Float(finalX, finalY);

    return finalPoint;
}

Edit: missed this one.

private float getMaxDist(Point2D.Float point) {
    float xMax;
    float yMax;
    if (Math.abs(point.x) > Math.abs(point.y)) {
        xMax = Math.signum(point.x);
        yMax = point.y * point.x / xMax;
    } else {
        yMax = Math.signum(point.y);
        xMax = point.x * point.y / yMax;
    }
    Point2D.Float maxPoint = new Point2D.Float(xMax, yMax);
    Point2D.Float center = new Point2D.Float(0, 0);
    return (float) center.distance(maxPoint);
}

It preserves the angle, but scales the distance from somewhere between 0 and boundary to between deadzone and boundary. The maximum distance varies since it's 1 on the sides and sqrt(2) in the corners, so scaling must be altered accordingly.

眼眸里的那抹悲凉 2024-08-22 11:00:52

我会尝试以不同的方式解决这个问题。 算法应

  1. 据我了解您的要求,如果操纵杆位置位于死区之外,
  2. 返回 x/y 值,如果操纵杆(部分)位于死区内,则返回 0/y、x/0 或 0/0

说操纵杆被向上推,但 x 位于定义的水平死区内,您需要结果是坐标 (0,y)。

因此,第一步,我将测试操纵杆坐标是否位于定义的死区内。对于圆来说,这非常简单,您只需将 x/y 坐标转换为距离(毕达哥拉斯)并检查该距离是否小于圆半径。

如果在外部,则返回 (x/y)。如果在内部,请检查 x 以及这些值是否在其水平或垂直死区内。

这是概述我的想法的草稿:

private Point convertRawJoystickCoordinates(int x, int y, double deadzoneRadius) {

    Point result = new Point(x,y); // a class with just two members, int x and int y
    boolean isInDeadzone = testIfRawCoordinatesAreInDeadzone(x,y,radius);
    if (isInDeadzone) {
      result.setX(0);
      result.setY(0);
    } else {
      if (Math.abs((double) x) < deadzoneRadius) {
        result.setX(0);
      }
      if (Math.abs((double) y) < deadzoneRadius) {
        result.setY(0);
      }
    }
    return result;         
}

private testIfRawCoordinatesAreInDeadzone(int x, int y, double radius) {
  double distance = Math.sqrt((double)(x*x)+(double)(y*y));
  return distance < radius;
}

编辑

上面的想法使用原始坐标,因此假设原始 x 值范围为 [-255,255],半径为 2 并且将操纵杆设置为 x 值( -3,-2,-1,0,1,2,3),它将产生序列 (-3,0,0,0,0,0,3)。因此,死区被清空,但有从 0 到 3 的跳跃。如果这是不需要的,我们可以将非死区从 ([-256,-radius],[radius,256])“拉伸”到(标准化)范围([-1,0],[0,1])。

所以我只需要标准化转换后的原始点:

private Point normalize(Point p, double radius) {
   double validRangeX = MAX_X - radius;
   double validRangeY = MAX_Y - radius;
   double x = (double) p.getX();
   double y = (double) p.getY();

   return new Point((x-r)/validXRange, (y-r)/validYRange);
}

简而言之:它将x轴和y轴的有效范围(范围减去死区半径)标准化为[-1,1],以便raw_x=radius转换为normalized_x= 0。

(该方法应该适用于正值和负值。至少我希望如此,目前我手头没有 IDE 或 JDK 来测试;))

I'd try to tackle the problem a bit differently. As I've understood your requirements, the algorithm should

  1. return the x/y values, if the joystick position is outside the deadzone
  2. return 0/y, x/0 or 0/0 if the joystick is (partially) inside the deadzone

Say the joystick is pushed up but x is inside the defined horizontal deadzone, you want the coordinate (0,y) as a result.

So in a first step, I'd test if the joystick coordinates are inside the defined deadzone. For a circle it's pretty easy, you just have to convert the x/y coordinates into a distance (Pythagoras) and check if this distance is less then the circles radius.

If it's outside, return (x/y). If it is inside, check for x and if the values are inside their horizontal or vertical deadzone.

Here's a draft to outline my idea:

private Point convertRawJoystickCoordinates(int x, int y, double deadzoneRadius) {

    Point result = new Point(x,y); // a class with just two members, int x and int y
    boolean isInDeadzone = testIfRawCoordinatesAreInDeadzone(x,y,radius);
    if (isInDeadzone) {
      result.setX(0);
      result.setY(0);
    } else {
      if (Math.abs((double) x) < deadzoneRadius) {
        result.setX(0);
      }
      if (Math.abs((double) y) < deadzoneRadius) {
        result.setY(0);
      }
    }
    return result;         
}

private testIfRawCoordinatesAreInDeadzone(int x, int y, double radius) {
  double distance = Math.sqrt((double)(x*x)+(double)(y*y));
  return distance < radius;
}

Edit

The above idea uses raw coordinates, so assume the raw x value range is [-255,255], the radius is 2 and you set the joystick to the x values (-3,-2,-1,0,1,2,3), it will produce the sequence (-3,0,0,0,0,0,3). So the deadzone is blanked, but there's a jump from 0 to 3. If that is unwanted, we can 'stretch' the non-deadzone from ([-256,-radius],[radius,256]) to the (normalized) range ([-1,0],[0,1]).

So I just need to normalize the converted raw points:

private Point normalize(Point p, double radius) {
   double validRangeX = MAX_X - radius;
   double validRangeY = MAX_Y - radius;
   double x = (double) p.getX();
   double y = (double) p.getY();

   return new Point((x-r)/validXRange, (y-r)/validYRange);
}

In brief: it normalizes the valid ranges (range minus deadzone radius) for x- and y-axis to [-1,1], so that raw_x=radius is converted to normalized_x=0.

(the method should work for positive and negative values. At least I hope it does, I have no IDE or JDK at hand at the moment to test ;) )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文