JavaScript 게임 엔진을 개발하려고 하는데이 문제가 발생했습니다.
- 내가 누르면 SPACE문자가 점프합니다.
- 내가 누르면 →문자가 오른쪽으로 이동합니다.
문제는 내가 오른쪽을 누른 다음 스페이스를 누르면 캐릭터가 점프 한 다음 움직이지 않는다는 것입니다.
이 keydown
기능을 사용하여 키를 누릅니다. 한 번에 여러 개의 키가 눌려 있는지 어떻게 확인할 수 있습니까?
JavaScript 게임 엔진을 개발하려고 하는데이 문제가 발생했습니다.
문제는 내가 오른쪽을 누른 다음 스페이스를 누르면 캐릭터가 점프 한 다음 움직이지 않는다는 것입니다.
이 keydown
기능을 사용하여 키를 누릅니다. 한 번에 여러 개의 키가 눌려 있는지 어떻게 확인할 수 있습니까?
답변:
참고 : 이제 keyCode는 더 이상 사용되지 않습니다.
개념을 이해하면 여러 번의 키 입력 감지가 쉽습니다.
내가하는 방식은 다음과 같습니다.
var map = {}; // You could also use an array
onkeydown = onkeyup = function(e){
e = e || event; // to deal with IE
map[e.keyCode] = e.type == 'keydown';
/* insert conditional here */
}
이 코드는 매우 간단합니다. 컴퓨터는 한 번에 하나의 키 입력 만 통과하므로 여러 키를 추적하기 위해 배열이 만들어집니다. 그런 다음 배열을 사용하여 한 번에 하나 이상의 키를 확인할 수 있습니다.
설명하기 위해 Aand 키 를 누르면 B각각 의 값이 true 또는 false로 평가 되는 keydown
이벤트를 발생 시킵니다. 이제 모두 와이 설정됩니다 . 당신이 놓아 때 의 에 대한 반대의 결과를 확인하기 위해 동일한 논리를 일으키는 이벤트가 발생 지금은 (A), 거짓 ,하지만 이후 (B) "다운"(그것의 keyup 이벤트를 트리거하지 않은) 여전히를, 그것은 사실로 남아 있습니다 .map[e.keyCode]
e.type == keydown
map[65]
map[66]
true
A
keyup
map[65]
map[66]
map
배열은 모두 이벤트를 통해, 다음과 같습니다 :
// keydown A
// keydown B
[
65:true,
66:true
]
// keyup A
// keydown B
[
65:false,
66:true
]
지금 할 수있는 두 가지가 있습니다.
A) 하나 이상의 키 코드를 신속하게 파악하려는 경우 나중에 참조 용으로 키 로거 ( 예 :)를 작성할 수 있습니다. html 요소를 정의하고 변수로 지정했다고 가정합니다 element
.
element.innerHTML = '';
var i, l = map.length;
for(i = 0; i < l; i ++){
if(map[i]){
element.innerHTML += '<hr>' + i;
}
}
참고 : id
속성 별로 요소를 쉽게 가져올 수 있습니다 .
<div id="element"></div>
이것은 자바 스크립트에서 쉽게 참조 할 수있는 html 요소를 만듭니다. element
alert(element); // [Object HTMLDivElement]
당신은 그것을 사용 document.getElementById()
하거나 $()
잡을 필요가 없습니다 . 그러나 호환성을 위해 jQuery를 사용하는 $()
것이 더 널리 권장됩니다.
스크립트 본문이 HTML 본문 뒤에 오는지 확인하십시오 . 최적화 팁 : 대부분의 유명 웹 사이트는 스크립트 태그 를 body 태그 뒤에 배치하여 최적화합니다. 스크립트 태그는 스크립트 다운로드가 완료 될 때까지 추가 요소가로드되지 않도록 차단하기 때문입니다. 내용보다 우선하면 내용을 미리로드 할 수 있습니다.
B (관심있는 곳) 한 번에 하나 이상의 키를 확인할 수 있습니다 /*insert conditional here*/
. 다음 예를 사용하십시오.
if(map[17] && map[16] && map[65]){ // CTRL+SHIFT+A
alert('Control Shift A');
}else if(map[17] && map[16] && map[66]){ // CTRL+SHIFT+B
alert('Control Shift B');
}else if(map[17] && map[16] && map[67]){ // CTRL+SHIFT+C
alert('Control Shift C');
}
편집 : 가장 읽기 쉬운 스 니펫이 아닙니다. 가독성이 중요하므로 눈에 쉽게 넣기 위해 다음과 같은 것을 시도해 볼 수 있습니다.
function test_key(selkey){
var alias = {
"ctrl": 17,
"shift": 16,
"A": 65,
/* ... */
};
return key[selkey] || key[alias[selkey]];
}
function test_keys(){
var keylist = arguments;
for(var i = 0; i < keylist.length; i++)
if(!test_key(keylist[i]))
return false;
return true;
}
용법:
test_keys(13, 16, 65)
test_keys('ctrl', 'shift', 'A')
test_key(65)
test_key('A')
이게 더 낫습니까?
if(test_keys('ctrl', 'shift')){
if(test_key('A')){
alert('Control Shift A');
} else if(test_key('B')){
alert('Control Shift B');
} else if(test_key('C')){
alert('Control Shift C');
}
}
(편집 끝)
이 예에 대한 검사 CtrlShiftA, CtrlShiftB및CtrlShiftC
그것은 그렇게 간단합니다 :)
일반적으로 코드, 특히 키 코드 (와 같은 // CTRL+ENTER
) 를 문서화하여 코드 를 기억하는 것이 좋습니다.
키 코드를 설명서와 동일한 순서로 넣어야합니다 ( CTRL+ENTER => map[17] && map[13]
, NOT map[13] && map[17]
). 이런 식으로 돌아가서 코드를 편집해야 할 때 혼동되지 않습니다.
서로 다른 양의 콤보를 확인하는 경우 (같은 CtrlShiftAltEnter과 CtrlEnter), 작은 콤보를 넣어 후에 더 큰 콤보, 또는 비슷한 충분한 경우 다른 작은 콤보는 큰 콤보를 우선합니다. 예:
// Correct:
if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!')
}
// Incorrect:
if(map[17] && map[13]){ // CTRL+ENTER
alert('You found me');
}else if(map[17] && map[16] && map[13]){ // CTRL+SHIFT+ENTER
alert('Whoa, mr. power user');
}else if(map[13]){ // ENTER
alert('You pressed Enter. You win the prize!');
}
// What will go wrong: When trying to do CTRL+SHIFT+ENTER, it will
// detect CTRL+ENTER first, and override CTRL+SHIFT+ENTER.
// Removing the else's is not a proper solution, either
// as it will cause it to alert BOTH "Mr. Power user" AND "You Found Me"
기본 창에서 포커스를 취하는 경고 또는 무엇이든 처리 할 때 map = []
조건이 완료된 후 어레이를 재설정 하도록 포함 할 수 있습니다 . alert()
이는와 같은 일부 가 기본 창에서 포커스를 빼고 '키업'이벤트가 트리거되지 않기 때문입니다. 예를 들면 다음과 같습니다.
if(map[17] && map[13]){ // CTRL+ENTER
alert('Oh noes, a bug!');
}
// When you Press any key after executing this, it will alert again, even though you
// are clearly NOT pressing CTRL+ENTER
// The fix would look like this:
if(map[17] && map[13]){ // CTRL+ENTER
alert('Take that, bug!');
map = {};
}
// The bug no longer happens since the array is cleared
여기에 해결책이 포함 된 성가신 것이 있습니다.
문제 : 브라우저에는 일반적으로 키 콤보에 대한 기본 작업 (예 : CtrlD책갈피 창 CtrlShiftC활성화 또는 maxthon에서 skynote 활성화)이 있기 때문에 return false
이후 에 추가하고 싶을 수도 있습니다 map = []
. 따라서 "파일 복제" 함수에 넣어되고 CtrlD대신 북마크에게 페이지를.
if(map[17] && map[68]){ // CTRL+D
alert('The bookmark window didn\'t pop up!');
map = {};
return false;
}
없이 return false
, 책갈피 창 것이다 사용자의 실망으로, 팝업.
좋아, 그래서 당신은 항상 그 시점에서 함수를 종료하고 싶지는 않습니다. 그것이 event.preventDefault()
기능이있는 이유 입니다. 브라우저가 기본 동작을 실행 하지 못하도록 인터프리터에게 알려주는 내부 플래그를 설정합니다. 그 후에는 기능 실행이 계속됩니다 (기능 return
은 즉시 종료됩니다).
사용할지 여부를 결정하기 전에이 차이를 이해 return false
하거나e.preventDefault()
event.keyCode
더 이상 사용되지 않습니다사용자 SeanVieiraevent.keyCode
는 더 이상 사용되지 않는 의견을 지적했습니다 .
거기에서 그는 훌륭한 대안을 제시했습니다. : for 또는 for event.key
와 같이 누르는 키의 문자열 표현을 반환합니다 ."a"
A"Shift"
Shift
나는 그 줄을 검사 하는 도구 를 요리했습니다 .
element.onevent
vs element.addEventListener
로 등록 된 핸들러 addEventListener
는 스택 될 수 있으며 등록 순서대로 호출되지만 .onevent
직접 설정 하는 것은 다소 공격적이며 이전에 가지고 있던 모든 것을 무시합니다.
document.body.onkeydown = function(ev){
// do some stuff
ev.preventDefault(); // cancels default actions
return false; // cancels this function as well as default actions
}
document.body.addEventListener("keydown", function(ev){
// do some stuff
ev.preventDefault() // cancels default actions
return false; // cancels this function only
});
.onevent
속성은 모든과의 동작을 재정의하는 것 ev.preventDefault()
하고 return false;
오히려 예측할 수 있습니다.
두 경우 모두 통해 등록 된 핸들러 addEventlistener
는 작성하기 쉽고 더 합리적인 것으로 보입니다.
이 또한 attachEvent("onevent", callback)
인터넷 익스플로러의 표준이 아닌 구현에서,하지만이되지 넘어, 심지어 자바 스크립트 (이라는 난해한 언어에 관한 관련이 없습니다 JScript를 ). 폴리 글 로트 코드를 최대한 피하는 것이 가장 좋습니다.
혼란 / 불만을 해결하기 위해이 추상화를 수행하는 "클래스"를 작성했습니다 ( pastebin link ).
function Input(el){
var parent = el,
map = {},
intervals = {};
function ev_kdown(ev)
{
map[ev.key] = true;
ev.preventDefault();
return;
}
function ev_kup(ev)
{
map[ev.key] = false;
ev.preventDefault();
return;
}
function key_down(key)
{
return map[key];
}
function keys_down_array(array)
{
for(var i = 0; i < array.length; i++)
if(!key_down(array[i]))
return false;
return true;
}
function keys_down_arguments()
{
return keys_down_array(Array.from(arguments));
}
function clear()
{
map = {};
}
function watch_loop(keylist, callback)
{
return function(){
if(keys_down_array(keylist))
callback();
}
}
function watch(name, callback)
{
var keylist = Array.from(arguments).splice(2);
intervals[name] = setInterval(watch_loop(keylist, callback), 1000/24);
}
function unwatch(name)
{
clearInterval(intervals[name]);
delete intervals[name];
}
function detach()
{
parent.removeEventListener("keydown", ev_kdown);
parent.removeEventListener("keyup", ev_kup);
}
function attach()
{
parent.addEventListener("keydown", ev_kdown);
parent.addEventListener("keyup", ev_kup);
}
function Input()
{
attach();
return {
key_down: key_down,
keys_down: keys_down_arguments,
watch: watch,
unwatch: unwatch,
clear: clear,
detach: detach
};
}
return Input();
}
이 클래스는 모든 것을 수행하지는 않으며 모든 가능한 유스 케이스를 처리하지는 않습니다. 나는 도서관 사람이 아닙니다. 그러나 일반적인 대화식 사용에는 문제가 없습니다.
이 클래스를 사용하려면 인스턴스를 작성하고 키보드 입력을 연관시킬 요소를 지정하십시오.
var input_txt = Input(document.getElementById("txt"));
input_txt.watch("print_5", function(){
txt.value += "FIVE ";
}, "Control", "5");
이것이 할 일은 요소에 새로운 입력 리스너를 첨부하고 #txt
(텍스트 영역이라고 가정하자) 키 콤보에 대한 감시 점을 설정하는 것 Ctrl+5
입니다. 때 모두 Ctrl
와 5
아래로, 당신이 전달 된 콜백 함수 (이 경우, 추가 기능 "FIVE "
텍스트 영역에)가 호출됩니다. 콜백은 name과 연결되어 print_5
있으므로 제거하려면 간단히 다음을 사용하십시오.
input_txt.unwatch("print_5");
요소 input_txt
에서 분리하려면 txt
:
input_txt.detach();
이 방법으로 가비지 수집은 객체 ( input_txt
)를 집어 버릴 수 있으며 오래된 좀비 이벤트 리스너가 남아 있지 않습니다.
철저한 설명을 위해 C / Java 스타일로 제공되는 클래스 API에 대한 빠른 참조가 있으므로 반환되는 내용과 예상되는 인수를 알 수 있습니다.
Boolean key_down (String key);
다운되면 반환
true
하고key
그렇지 않으면 false를 반환합니다.Boolean keys_down (String key1, String key2, ...);
true
모든 키key1 .. keyN
가 다운 되면 반환 하고 그렇지 않으면 false를 반환합니다.void watch (String name, Function callback, String key1, String key2, ...);
모두를 누르면
keyN
콜백이 트리거 되도록 "감시 점"을 만듭니다.void unwatch (String name);
이름을 통해 상기 감시 점을 제거합니다
void clear (void);
"키 다운"캐시를 지 웁니다.
map = {}
위와 동일void detach (void);
부모 요소에서
ev_kdown
및ev_kup
리스너를 분리하여 인스턴스를 안전하게 제거 할 수 있습니다.
2017-12-02 업데이트 이것을 github에 게시하라는 요청에 따라 gist를 만들었습니다 .
2018-07-21 업데이트 나는 한동안 선언적 스타일 프로그래밍을 해왔으며,이 방법은 내가 개인적으로 좋아하는 것입니다 : fiddle , pastebin
일반적으로 현실적으로 원하는 경우 (ctrl, alt, shift)와 함께 작동하지만 a+w
동시에 충돌 해야하는 경우 접근 방식을 "결합"하는 것이 어렵지 않습니다. 다중 키 조회.
나는이 철저하게 설명 된 답변 미니 블로그가 도움 이되기를 바랍니다 :)
return false
대preventDefault()
keyCode
더 이상 사용되지 않습니다 . 전환 key
하면 키의 실제 문자 표현을 얻을 수 있습니다.
myString[5]
와) 동일 하다는 것을 알고 있습니까? 5[myString]
심지어 컴파일 경고도 표시하지 않습니다 ( -Wall -pedantic
때문이다 pointer[offset]
표기하고, 포인터를 걸리는 오프셋을 추가 한 다음 그 결과를 역 참조 myString[5]
와 동일 *(myString + 5)
.
당신은 사용해야 를 keyDown 누른 키를 추적하는 이벤트를, 그리고 당신은 사용해야 의 keyup의 키가 해제 될 때 추적 할 이벤트를.
이 예를 참조하십시오 : http://jsfiddle.net/vor0nwe/mkHsU/
(업데이트 : jsfiddle.net이 보석으로 여기는 경우를 대비하여 여기에서 코드를 재현하고 있습니다.) HTML :
<ul id="log">
<li>List of keys:</li>
</ul>
... 그리고 자바 스크립트 (jQuery 사용) :
var log = $('#log')[0],
pressedKeys = [];
$(document.body).keydown(function (evt) {
var li = pressedKeys[evt.keyCode];
if (!li) {
li = log.appendChild(document.createElement('li'));
pressedKeys[evt.keyCode] = li;
}
$(li).text('Down: ' + evt.keyCode);
$(li).removeClass('key-up');
});
$(document.body).keyup(function (evt) {
var li = pressedKeys[evt.keyCode];
if (!li) {
li = log.appendChild(document.createElement('li'));
}
$(li).text('Up: ' + evt.keyCode);
$(li).addClass('key-up');
});
이 예에서는 어떤 키를 눌렀는지 추적하기 위해 배열을 사용하고 있습니다. 실제 응용 프로그램에서는 delete
관련 키가 해제되면 각 요소에 원할 수 있습니다 .
이 예제에서는 jQuery를 사용하여 쉽게 작업 할 수 있지만 '원시'Javascript로 작업 할 때도 개념이 잘 작동합니다.
onblur
배열에서 누른 키를 모두 제거 하는 이벤트 핸들러를 추가 할 수도 있습니다 . 초점을 잃은 후에는 모든 키를 다시 눌러야합니다. 불행히도에 해당하는 JS는 없습니다 GetKeyboardState
.
document.onkeydown = keydown;
function keydown (evt) {
if (!evt) evt = event;
if (evt.ctrlKey && evt.altKey && evt.keyCode === 115) {
alert("CTRL+ALT+F4");
} else if (evt.shiftKey && evt.keyCode === 9) {
alert("Shift+TAB");
}
}
이 방법을 사용했습니다 (Shift + Ctrl을 눌렀을 때 확인해야합니다).
// create some object to save all pressed keys
var keys = {
shift: false,
ctrl: false
};
$(document.body).keydown(function(event) {
// save status of the button 'pressed' == 'true'
if (event.keyCode == 16) {
keys["shift"] = true;
} else if (event.keyCode == 17) {
keys["ctrl"] = true;
}
if (keys["shift"] && keys["ctrl"]) {
$("#convert").trigger("click"); // or do anything else
}
});
$(document.body).keyup(function(event) {
// reset status of the button 'released' == 'false'
if (event.keyCode == 16) {
keys["shift"] = false;
} else if (event.keyCode == 17) {
keys["ctrl"] = false;
}
});
완전한 예제 코드가 필요한 사람. 오른쪽 + 왼쪽 추가
var keyPressed = {};
document.addEventListener('keydown', function(e) {
keyPressed[e.key + e.location] = true;
if(keyPressed.Shift1 == true && keyPressed.Control1 == true){
// Left shift+CONTROL pressed!
keyPressed = {}; // reset key map
}
if(keyPressed.Shift2 == true && keyPressed.Control2 == true){
// Right shift+CONTROL pressed!
keyPressed = {};
}
}, false);
document.addEventListener('keyup', function(e) {
keyPressed[e.key + e.location] = false;
keyPressed = {};
}, false);
에 keypress
Event
핸들러를 추가하려고 합니다 keydown
. 예 :
window.onkeydown = function() {
// evaluate key and call respective handler
window.onkeypress = function() {
// evaluate key and call respective handler
}
}
window.onkeyup = function() {
window.onkeypress = void(0) ;
}
이것은 단지 패턴을 설명하기위한 것입니다. 여기서 자세히 설명하지 않겠습니다 (특히 브라우저 별 level2 + Event
등록에 대해서는 다루지 않음 ).
이것이 도움이되는지 여부를 다시 게시하십시오.
누른 키 중 하나가 Alt / Crtl / Shift 인 경우이 방법을 사용할 수 있습니다.
document.body.addEventListener('keydown', keysDown(actions) );
function actions() {
// do stuff here
}
// simultaneous pressing Alt + R
function keysDown (cb) {
return function (zEvent) {
if (zEvent.altKey && zEvent.code === "KeyR" ) {
return cb()
}
}
}
$(document).ready(function () {
// using ascii 17 for ctrl, 18 for alt and 83 for "S"
// ctr+alt+S
var map = { 17: false, 18: false, 83: false };
$(document).keyup(function (e) {
if (e.keyCode in map) {
map[e.keyCode] = true;
if (map[17] && map[18] && map[83]) {
// Write your own code here, what you want to do
map[17] = false;
map[18] = false;
map[83] = false;
}
}
else {
// if u press any other key apart from that "map" will reset.
map[17] = false;
map[18] = false;
map[83] = false;
}
});
});
이것은 보편적 인 방법은 아니지만 경우에 따라 유용합니다. CTRL+ something또는 Shift+ something또는 CTRL+ Shift+ 등과 같은 조합에 유용합니다 something.
예 : CTRL+를 사용하여 페이지를 인쇄하려는 P경우 첫 번째로 누른 키 CTRL뒤에 항상 이 P있습니다. CTRL+ S, CTRL+ U및 기타 조합과 동일합니다 .
document.addEventListener('keydown',function(e){
//SHIFT + something
if(e.shiftKey){
switch(e.code){
case 'KeyS':
console.log('Shift + S');
break;
}
}
//CTRL + SHIFT + something
if(e.ctrlKey && e.shiftKey){
switch(e.code){
case 'KeyS':
console.log('CTRL + Shift + S');
break;
}
}
});
Easiest, and most Effective Method
//check key press
function loop(){
//>>key<< can be any string representing a letter eg: "a", "b", "ctrl",
if(map[*key*]==true){
//do something
}
//multiple keys
if(map["x"]==true&&map["ctrl"]==true){
console.log("x, and ctrl are being held down together")
}
}
//>>>variable which will hold all key information<<
var map={}
//Key Event Listeners
window.addEventListener("keydown", btnd, true);
window.addEventListener("keyup", btnu, true);
//Handle button down
function btnd(e) {
map[e.key] = true;
}
//Handle Button up
function btnu(e) {
map[e.key] = false;
}
//>>>If you want to see the state of every Key on the Keybaord<<<
setInterval(() => {
for (var x in map) {
log += "|" + x + "=" + map[x];
}
console.log(log);
log = "";
}, 300);