私の問題: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)のジョイスティックのたわみに対してこれが出力するものは次のとおりです。
非正方形のジョイスティックのたわみ。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();
}
}