Basically, here is my code:
bool Player::onCollision(BaseOBJ* obj)
{
if(obj->name == "phys_static")
{
jumping = true;
}
return true;
}
void Player::jump()
{
if(jumping == true)
{
ApplyForce(Vec2(0.f, -13000.f), Vec2(0.f, 0.f));
jumping = false;
}
}
onCollision is called first, when there is a collision. It is not called if there has not been any new collisions (ex. only jumps would register, not moving from side to side)
It normally works fine, however if I hold the jump key all the way through the jump (from when it is launched to when it touches the ground) I cannot jump again. Any solutions?
An often overlooked issue with holding down a key is that your operating system will convert this into fast, repeated presses.
Example:
In your text editor, hold the A key. One 'a' will appear, and then, after a slight delay:
aaaaaaaaaaaaa (Many repeated 'A' key presses are sent to the active application.)
Is your jump function being called repeatedly and somehow messing up how you intended your jump checking to work?
If not, please answer some of the questions that earlier commenters have presented.
P.S. Suggestion on the side: it appears to me that 'jumping' should be named 'canJump' or 'allowJumping'. This avoids a lot of confusion. Right now it sounds like it indicates whether you are jumping, whereas you actually use it to indicate whether you can jump.
Related
To handle text input I've set up a char-event callback with glfwSetCharCallback, and to handle non-text keypresses (arrow keys & hotkeys) I've set up a key-event callback with glfwSetKeyCallback.
What happens in this situation is that for a key press of a character key, I get two calls, one in the key-event callback, and then one in the char-event callback. This can cause unwanted effects - for example let's suppose the user configured the key "a" to enter "Append Mode" of a text editor - after it enters the mode it will also enter the character "a".. Is there a good way to handle this?
So far I've relied on both events arriving together before glfwPollEvents returns, and have merged them. But I get reports that this scheme doesn't work well on some Ubuntu systems..
I've been having trouble with this one as well. After some rudimentary debugging I found that if you press, hold then release a 'typable' key (meaning a key which may fire both the glfwKeyCallback and glfwCharCallback), the output is as follows:
KeyCallback - pressed
CharCallback - typed
KeyCallback - repeated
CharCallback - typed
(3. and 4. repeat until key is released)
KeyCallback - released
With this, and judging from the fact that there is a 0ms delay between the two events firing, they're probably fired sequentially. The solution I came up with (is rather janky), and involves creating some sort of KeyEvent structure:
(examples are in C++)
enum KeyEventType
{
Pressed,
Repeated,
Released
}
struct KeyEvent
{
KeyEventType Type;
int Key;
unsigned char Codepoint;
bool IsTyped;
}
and store it along with an index variable, such as
[main/input class]
std::vector<KeyEvent> m_KeyEvents;
size_t m_LastKeyEventIndex;
in the main file.
Then, when the glfwKeyCallback fires, push a new KeyEvent into the vector:
[glfwKeyCallback]
KeyEventType type = (action == GLFW_PRESS ? KeyEventType::Pressed : (action == GLFW_REPEAT ? KeyEventType::Repeated : KeyEventType::Released));
KeyEvent event = KeyEvent(type, key);
m_KeyEvents.push_back(event);
m_LastKeyEventIndex = m_KeyEvents.size() - 1;
and if the glfwCharCallback fires, we know from the debugging that it should be (immediately) after the corresponding keyCallback event, so you can modify the last added entry in the vector to add the codepoint and mark it as a 'typed' event, after-the-fact. This also gives the added benefit of tying the actual key that was pressed to the generated codepoint, which could come in useful.
[glfwCharCallback]
m_KeyEvents.at(m_LastKeyEventIndex).Codepoint = codepoint;
m_KeyEvents.at(m_LastKeyEventIndex).IsTyped = true;
Finally, in the main loop when you go to call glfwPollEvents(), process all those pending KeyEvents and then clear the vector and reset the index.
I haven't fully tested this yet, but some very rudimentary debugging shows this as a promising solution, resulting in the following*:
*I'm using a custom Key enum in place of the int Key. You could probably use glfwGetKeyName() to get the printable key name, however this resulted in exceptions for me when pressing some keys.
I'm creating this 2D game and I'm having problems with the Jump function. It works, the character jumps up and down, but I would like to be able to jump and then move the character while its in the air,e.g. so it can jump onto a platform. I'm using SDLK...
case SDL_KEYDOWN:
switch (event.key.keysym.sym){
RArrow = (event.button.button = SDLK_RIGHT);
Jump = (event.button.button = SDLK_SPACE);
if((RArrow) && (Jump))
{
if(g->getPlayer()->worldY = GROUND_LEVEL)
{
g->getPlayer()->jump();
g->getPlayer()->move(10);
}
}
break;
This is one of the ways I've tried. I've also tried to make a switch function inside the jump case to navigate left or right but I either didn't code it right or just didn't work.
I'm unfamiliar with SDLK and how this actually is done in real game development.
However one way I've implemented this is to see if both keys are pressed on each game tick. So if w+d is pressed the character would go up and right at the same time. But it's a hackish solution in my opinion.
With the implementation listed, looks like the character will only move right when they are on the ground level. You need to change your code so that every tick, the game checks if the character is jumping and if so, moves the character in the direction it is facing/jumping. This code may need to live outside of your event handler.
Ideally, your keyboard event handler would just set a flag on the character indicating that they are jumping and which direction, then in your game loop you call an update() method which actually handles the jumping (and stops jumping when the character collides with the world geometry, like a floor or wall).
Hope this helps!
EDIT:
Without seeing the rest of your code, I can't say much more. I'm also not great with C++, which I'm assuming this uses. One way you could implement this is:
Replace "g->getPlayer()->jump()" with "g->getPlayer()->setJumping()".
Implement "setJumping()" on the Character to set a jumping flag.
In the game loop, call "g->getPlayer()->updatePos()"
Implement "updatePos()" on the Character to update the character's position. In this, check the jumping flag, and if it's true update the position based on your jumping algorithm.
I'm porting a game from Ruby to C++. There is a main render loop that updates and draw the content. Now let's say that during the game, you want to select an item another screen. The way it's done in the original code is to do Item item = getItemFromMenu(); getItemFromMenu is a function that will open the menu and do have its own update/render loop, which mean that during the whole time the player has this other screen open, you are in a nested render loop. I feel like this is a bad method but I'm not sure why. On the other hand it's very handy because I can open the menu with just 1 function call and so the code is localized.
Any idea if this is a bad design or not?
I hesitated to post it on gamedev, but since this is mostly a design issue I posted it here
edit : some pseudo-code to give you an idea:
The usual loop in the main part of the code:
while(open) {
UpdateGame();
DrawGame();
}
now inside UpdateGame() i would do something like:
if(keyPressed == "I") {
Item& item = getItemFromInventory();
}
And getItemFromInventory():
while(true) {
UpdateInventory();
if(item_selected) return item;
DrawInventory();
}
A good way to handle something like this would be to replace the DrawInventory() call with something like InvalidateInventory(), which will mark the current graphical state of the inventory as outdated and request it to be redrawn during the next frame rendering (which'll happen pretty soon after when the main loop gets to DrawGame()).
This way, you can keep running through the main loop, but the only parts of the screen that get looked at for redrawing are the ones that have been invalidated, and during normal gameplay you can invalidate your (2/3)D environment as a normal part of processing, but then inside the inventory you can always mark only inventory assets as needing to be redrawn, which minimises overhead.
The other part of your inner loop, UpdateInventory(), can be a part of UpdateGame() if you use a flag to indicate the current game state, something like:
UpdateGame()
{
switch(gameState)
{
case INVENTORY:
UpdateInventory();
break;
case MAIN:
default:
UpdateMain();
break;
}
}
If you really wanted, you could also apply this to drawing:
DrawGame()
{
switch(gameState)
{
case INVENTORY:
DrawInventory();
break;
case MAIN:
default:
DrawMain();
break;
}
}
But I think drawing should be encapsulated and you should tell it which part of the screen, rather than which separate area of the game, needs to be drawn.
What you've created with your nested render loop is functionally a state machine (as most game render loops tend to be). The problem with the nested loop is that many times you'll want to do the same sorts of things in your nested loop as your outer loop (process input, handle IO, update debug info etc).
I've found that it's better to have one render loop and use a finite state machine (FSM) to represent your actual states. Your states might look like:
Main menu state
Options menu state
Inventory state
World view state
You hook up transitions between states to move between them. The player clicking a button might trigger the transition which could play an animation or otherwise, then move to the new state. With a FSM your loop might look like:
while (!LeaveGame()) {
input = GetInput();
timeInfo = GetTimeInfo();
StateMachine.UpdateCurrentState(input, timeInfo);
StateMachine.Draw();
}
A full FSM can be a bit heavyweight for a small game so you can try a simplified state machine using a stack of game states. Every time the user does an action to transition to a new state you push the state on a stack. Likewise when they leave a state you pop it off. Only the top of the stack typically receives input and the other items on the stack may/may not draw (depending on your preference). This is a common approach and has some upsides and downsides depending on who you talk to.
The simplest option of all is to just throw a switch statement in to pick which render function to use (similar to darvids0n's answer). If you're writing an arcade clone or a small puzzle game that would do just fine.
I'm working on a c++ project for school, and my implementation at this moment requires mouse input for controlling a ship (It's supposed to be a remake of Tyrian). Everything is working fine, except for the first time I move the mouse, it has an offset depending on where the mouse was when I started the game. I guess this is because I'm using SDL_GetRelativeMouse but how can I prevent it?
You may wish to initialize the mouse to a good known position when the application begins, possibly right before events callbacks are initialized.
Making sure the mouse is within the bounds of the window may also be appropriate. It isn't really relevant to the application outside of its boundaries anyway.
This is what I do to toggle mousegrab in a FPS-type application:
if(event.key.keysym.sym == SDLK_z)
{
if( mouse_grabbed )
{
SDL_WM_GrabInput(SDL_GRAB_OFF);
SDL_WarpMouse( display->w/2, display->h/2 );
SDL_ShowCursor(1);
}
else
{
SDL_ShowCursor(0);
SDL_WM_GrabInput(SDL_GRAB_ON);
int tx,ty;
SDL_GetRelativeMouseState(&tx, &ty);
}
mouse_grabbed = !mouse_grabbed;
}
Consuming a mouse update via the dummy SDL_GetRelativeMouseState() call was the important part.
For the moment I'll just suppress the first time mouse movement is detected. This seems to work, but it seems a rather unprofessional approach.
I use this:
Sdl.SDL_ShowCursor(Sdl.SDL_DISABLE);
Sdl.SDL_WM_GrabInput(Sdl.SDL_GRAB_ON);
e=new Sdl.SDL_Event();
pollOne();
.
.
.
private void pollOne(){
while(Sdl.SDL_PollEvent(out e)==1){
switch(e.type){
case Sdl.SDL_MOUSEMOTION:
float throwAway=((float)e.motion.xrel*headSens);
break;
}
}
}
Basically, when initializing the mouse (grabbing it and making it invisible) call pollOne to throw the first event away. Every event captured hereafter in the main event processing function called by the main loop is with mouse at center screen.
I am writing a game and am trying to make a person move with the arrow keys.
I have this code
if (Key_Down(DIK_DOWN))
{movedown(player)}
This works but as I want the player to take four steps every time the key is pressed I created and animation loop. so the player cannot input any more move commands until the animation is over and they have taken four steps I made the code this.
if(player.move == false)
{
if (Key_Down(DIK_DOWN))
{movedown(player)}
}
The problem is that now once a button is pressed the program acts like the button is held down and the player keeps moving until another direction is pressed.
Can anyone explain what the outer loop has done to the code and fix the problem ?
it is programmed in visual c++ 2005 and direct x 9.c
Edit:
If I remove the outer loop then the button press is only registered once so I don't think it is the movedown function.
Simply keep track of the keystate in a variable. So you can reset a move counter. Something like this, combining both:
int MovingDown = 0;
...
if (!Key_Down(DIK_DOWN)) MovingDown = 0;
else if (MovingDown < 4) {
MovingDown++;
movedown(player);
}
Hard to tell without seeing more of your codebase but I would guess that you're not re-setting player.move after you've moved the player. Have you tried sticking some break poins in there to see what is being called and why?
If you remove the if(player.move == false) line does it have the same issue? ..or have you changed something elsewhere? If you could post more of your codebase like the whole movedown function and anythnig else which interacts (e.g. the animation code) it would help us help you...
[Edit] Is movedown a macro? Maybe you've got some peculiar side effect eminating from that? If not, it ought to have a semi colon after the function call.
What does KEY_DOWN return? Is that an API call? Try testing against the exact value you expect it to return?