java - 操纵杆死区计算

标签 java trigonometry

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

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

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

方形死区很简单。在下面的代码中,x 和 y 是从 -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);
}

这是操纵杆在极端情况下偏转的结果(死区=0.25):

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

如您所见,偏转没有延伸到角落。 IE,如果 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();
 }
}

最佳答案

如果你有一个圆形死区,.71 实际上是 0.70710678 或 2 的平方根的一半 计算归功于毕达哥拉斯定理

关于java - 操纵杆死区计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1944499/

相关文章:

c# - 与 Math.Round() 不一致

objective-c - acosf() 返回 NaN

c++ - OpenGL圆形绘图得到一个椭圆

使用泰勒展开的 sin(x) 汇编代码

java - 即使启用所有属性后,Hibernate 也不记录触发的 SQL 查询

java - Android 警报对话框颜色已关闭

java - 如何访问 jni 中 jobjectarray 中的元素

javascript - 如何通过给定的 2 个点计算弧的 'start' 和 'end' Angular ?

Java - GridLayout 调整 JPanel 大小

java - GridBagLayout 不工作