Muitos de nós se relacionam calorosamente com os videogames da velha escola lançados na virada do século. Eles têm uma excelente atmosfera, dinâmica frenética e muitas soluções originais que não se tornam obsoletas após décadas. No entanto, hoje em dia, a visão da interface do jogo mudou um pouco - corredores lineares substituíram níveis confusos, a regeneração substituiu os kits de primeiros socorros e, em vez de uma longa fila de teclas de 0 a 9 para selecionar um arsenal, primeiro a roda do mouse e depois a roda virtual, chegaram. É sobre ele hoje que será discutido.
Resumo histórico
Anteriormente, durante o surgimento do gênero do atirador, a questão do controle do mouse não foi levantada - apenas o teclado foi usado para controlar o protagonista. Além disso, também não havia um formato de gerenciamento único - o WASD tornou-se o padrão um pouco mais tarde. Você pode ler mais sobre os antigos layouts de teclado para jogos aqui .
Assim, naqueles jogos em que a capacidade de selecionar equipamentos foi implementada (Doom, Wolfenstein, Quake etc.), foi implementada da única maneira intuitiva na época - usando as teclas numéricas do teclado. E por muitos anos esse método foi o único.
Então, no final dos anos 90, tornou-se possível trocar de armas com a roda do mouse.
Não foi possível encontrar informações inequívocas sobre esse tópico, mas no CS 1.6 esse recurso foi ativado no console. No entanto, esses precedentes podem ter existido antes - nesse caso, indique isso nos comentários ou no PM. Mas, na forma usual de roda de armas em nosso tempo, ela só foi usada com o Crysis e seu menu Suit. Embora tentativas de fazer algo semelhante tenham começado no HL2, a “roda” foi para as massas apenas no final dos anos 2000 e agora é predominante. .
No entanto, este é apenas um resumo histórico, de interesse apenas como história. No âmbito deste artigo, não haverá longas discussões sobre os motivos da popularidade desta ou daquela solução. além de provar qual é o melhor seletor. Simplesmente porque o seguinte descreverá o processo de adaptação do bom e velho Doom à escolha de armas com o mouse.
Estabelecendo objetivos
Para implementar o WW, você precisa, de alguma forma, interceptar o movimento do mouse, acompanhar seu movimento enquanto a tecla seletora é mantida pressionada e, quando solta, emular um clique no botão correspondente ao setor selecionado.
Para isso, usei a linguagem Java, em particular, a interceptação de chave é realizada usando a biblioteca jnativehook, e pressionar é devido ao awt.Robot. O processamento dos ganchos recebidos não é difícil, portanto, é feito manualmente.
Implementação
Anteriormente, foram desenvolvidas classes que definem pares de coordenadas para determinar o vetor de deslocamento.
Em particular, a classe Shift permite armazenar um vetor bidimensional, bem como determinar seu comprimento, e a classe NormalisedShift, projetada para armazenar um vetor normalizado, entre outras coisas, permite determinar o ângulo entre o vetor interceptado e o vetor (1,0).
Cabeçalho do spoiler
class Shift{
int xShift;
int yShift;
public int getxShift() {
return xShift;
}
public int getyShift() {
return yShift;
}
public void setxShift(int xShift) {
this.xShift = xShift;
}
public void setyShift(int yShift) {
this.yShift = yShift;
}
double getLenght(){
return Math.sqrt(xShift*xShift+yShift*yShift);
}
}
class NormalisedShift{
double normalizedXShift;
double normalizedYShift;
double angle;
NormalisedShift (Shift shift){
if (shift.getLenght()>0)
{
normalizedXShift = -shift.getxShift()/shift.getLenght();
normalizedYShift = -shift.getyShift()/shift.getLenght();
}
else
{
normalizedXShift = 0;
normalizedYShift = 0;
}
}
void calcAngle(){
angle = Math.acos(normalizedXShift);
}
double getAngle(){
calcAngle();
return (normalizedYShift<0?angle*360/2/Math.PI:360-angle*360/2/Math.PI);
};
};
Eles não são de interesse particular e apenas as linhas 73-74, que normalizam o vetor, requerem comentário. Entre outras coisas, o vetor é invertido. o quadro de referência muda - o fato é que, do ponto de vista do software e do ponto de vista da matemática familiar, os vetores são tradicionalmente direcionados de maneira diferente. É por isso que os vetores da classe Shift têm a origem no canto superior esquerdo e a classe NormalizedShift tem o canto inferior esquerdo.
Para implementar o trabalho do programa, foi implementada a classe Wheel, que implementa as interfaces NativeMouseMotionListener e NativeKeyListener. O código está sob o spoiler.
Cabeçalho do spoiler
public class Wheel implements NativeMouseMotionListener, NativeKeyListener {
final int KEYCODE = 15;
Shift prev = new Shift();
Shift current = new Shift();
ButtomMatcher mathcer = new ButtomMatcher();
boolean wasPressed = false;
@Override
public void nativeMouseMoved(NativeMouseEvent nativeMouseEvent) {
current.setxShift(nativeMouseEvent.getX());
current.setyShift(nativeMouseEvent.getY());
}
@Override
public void nativeMouseDragged(NativeMouseEvent nativeMouseEvent) {
}
@Override
public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {
}
@Override
public void nativeKeyPressed(NativeKeyEvent nativeKeyEvent) {
if (nativeKeyEvent.getKeyCode()==KEYCODE){
if (!wasPressed)
{
prev.setxShift(current.getxShift());
prev.setyShift(current.getyShift());
}
wasPressed = true;
}
}
@Override
public void nativeKeyReleased(NativeKeyEvent nativeKeyEvent) {
if (nativeKeyEvent.getKeyCode() == KEYCODE){
Shift shift = new Shift();
shift.setxShift(prev.getxShift() - current.getxShift());
shift.setyShift(prev.getyShift() - current.getyShift());
NormalisedShift normalisedShift = new NormalisedShift(shift);
mathcer.pressKey(mathcer.getCodeByAngle(normalisedShift.getAngle()));
wasPressed = false;
}
}
Vamos descobrir o que está acontecendo aqui.
A variável KEYCODE armazena o código da chave usada para chamar o seletor. Normalmente, isso é TAB, mas, se necessário, você pode alterá-lo no código ou, idealmente, retirá-lo do arquivo de configuração.
prev armazena a posição do cursor do mouse que estava no momento em que o seletor foi chamado. Surrent mantém a posição atual do cursor em um determinado momento. Assim, quando você solta a tecla seletora, os vetores são subtraídos e o deslocamento do cursor durante a espera da tecla seletora é gravado na variável shift.
Então, na linha 140, o vetor normaliza, isto é, reduzido para formar quando seu comprimento é próximo a um. Depois disso, o vetor normalizado é transmitido ao combinador, que estabelece a correspondência entre o código da tecla a ser pressionado e o ângulo de rotação do vetor. Por motivos de legibilidade, o ângulo é convertido em graus, bem como - é orientado ao longo de um círculo unitário completo (o acos funciona apenas com ângulos de até 180 graus).
A classe ButtonMatcher define a correspondência entre o ângulo e o código da chave selecionado.
Cabeçalho do spoiler
class ButtomMatcher{
Robot robot;
final int numberOfButtons = 6;
int buttonSection = 360/numberOfButtons;
int baseShift = 90-buttonSection/2;
ArrayList<Integer> codes = new ArrayList<>();
void matchButtons(){
for (int i =49; i<55; i++)
codes.add(i);
}
int getCodeByAngle(double angle){
angle= (angle+360-baseShift)%360;
int section = (int) angle/buttonSection;
System.out.println(codes.get(section));
return codes.get(section);
}
ButtomMatcher() {
matchButtons();
try
{
robot = new Robot();
}
catch (AWTException e) {
e.printStackTrace();
}
}
void pressKey(int keyPress)
{
robot.keyPress(keyPress);
robot.keyRelease(keyPress);
}
}
Além disso, a variável numberOfButtons determina o número de setores e seus botões correspondentes, baseShift define o ângulo de rotação (em particular, fornece simetria sobre o eixo vertical e gira a roda 90 graus para que a arma branca fique por cima), e a matriz de códigos armazena códigos teclas - caso os botões sejam alterados e os códigos não sejam consecutivos. Em uma versão mais elaborada, seria possível extraí-los do arquivo de configuração, mas com o layout padrão das teclas - a versão atual é bastante viável.
Conclusão
No âmbito deste artigo, foi descrita a possibilidade de personalizar a interface dos atiradores clássicos para os padrões modernos. Obviamente, não adicionamos nenhum kit de primeiros socorros ou linearidade aqui - existem muitos mods para isso, mas muitas vezes é nesses detalhes que uma interface amigável e conveniente se encontra. O autor percebe que provavelmente não descreveu a melhor maneira de alcançar o resultado desejado e também espera nos comentários uma foto com um pão e um trólebus, mas, no entanto, foi uma experiência interessante que, talvez, incentive alguns jogadores a descobrir O maravilhoso mundo de Java.
Críticas construtivas são bem-vindas.
Código fonte