Hello everyone! I’m trying to teach myself Coding (using Pascal) and I thought I fiddle around in Castle Game Engine just for the fun of it.
I’m trying to write a behaviour that handles movement of a transform - pretty standard stuff WASD-Controls - you know it.
However I have trouble accessing the container (for Container.Pressed[keyXxx]) from within the behavior. There was a similar post to mine from 2021 but it seems that some stuff has changed about containers, so i figured I’d ask again. How do i handle button presses from within a behavior in maybe a best-practice kind of way? Examples would be great, too!
Although I’m not a CGE expert, I can share a few solutions to your problem. Depending of what you want to achieve:
- If you want to control the camera and the player, you can use the existing behaviors: TCastleWalkNavigation or TCastleThridPersonNavigation. Castle’s Fly Navigation is basically the Walk with Gravity turned off.
- If you want to implement your own navigation look at the code of TCastleWalkNavigation for clues, it’s located in a unit called CastleCameras (/src/transform/castlecameras.pas). It contains functions
function Press(const Event: TInputPressRelease): boolean; override; function Release(const Event: TInputPressRelease): boolean; override;
- If you want to control an object selected by the player, you may find it easier without behaviors, by using method
Press
- example of using this function is in the FPS game template: Create new project, choice the fps template, open the GameViewMain.pas and find the function, it contains enough info inside its body to get you moving.
In that approach you may have a variable storing the selected object, then adding WASD support inside the function (writing theif ... then
clause for each button) you can move the object by changing its Translation.x / .y / .z. Note that objects directions depends on rotation (including the parent rotation). If the world is the parent and object’s rotation is zero, then you move along world X, Y, Z. You can also use methodMove
orMoveTo
- every transform (game object) has it.
If you want it to move in directions affected by camera/player angle of viewing, you need to turn the object, assign obj.Direction := Camera.Direction - If the object is held by the player - eg. player picked it from world, then you can make the player a Parent of the object. Which means it moves wherever the player moves, and rotates with the player. When the player release the object (put it down) you may need to change the Parent back, and translate the object’s position from player-based to world based. Translation is not difficult, but you need to remember to do it. Alternatively, if you don’t want to change the objects parent to player, and then back to original parent, you could give the player a ghost image of the object. So the original object stays where it was until player finish the manipulation.
Basically the TCastleWalkNavigation and MainView.Press are the things you’d want to experiment, IMHO.
The Press
method is easier to implement than behaviors for controlling objects different than the player/camera, because if you create a behavior to move the selected object, you have to swap the behavior from object to object at selection change or make the behavior aware that it should only react when the object is selected (it has a property named MoveAllowed
).
I understand my answer is quite general, and for sure it’s not very beginner friendly but everything depends on what exactly you want to do, and how well you know programming at all (not necessarily Pascal). Again - I’m not CGE pro, so maybe others have better solutions that I haven’t seen yet.
Hey thanks for the quick reply. After having a look at the TCastleWalkNavigation class and some other stuff in the mentioned unit i quickly decided, that most of the stuff goes way above my head . I was just looking for an easy way to process user input and was under the impression that i could use a behavior a bit like a scripting component in unity where I would then “simply” add my basic input key handlers and stuff.
I’m going to play around with the existing behavior and see if it does what I want or else try (3).
Thanks again!
Start with a demo that already has the TCastleWalkNavigation implemented. WyrdForest is one, or the Terrain demo. It isn’t really very complicated if you have something to start with to see how it works.
I will try my stab at the answer, which may a bit rephrase other answers above too
-
First of all, maybe you don’t need to handle inputs in a
TCastleBehavior
descendant. The most straightforward approach to handle inputs is to override view events likeUpdate
(where you can checkContainer.Pressed
) orPress
/Release
(which gets a parameterEvent: TInputPressRelease
, saying what was pressed / released). See Designing user interface and handling events (press, update) within the view | Manual | Castle Game Engine .To be more general, all user interface components (
TCastleUserInterface
descendants), of whichTCastleView
is only one possibility, are notified about presses and have access to parentContainer
without any fuss. -
Naturally these methods in a view could get / set / call appropriate stuff in your chosen behavior. E.g. your behavior descendant could have methods like
procedure Shoot;
or fieldsRunning: Boolean
, that are set / called by the view, and your behavior reacts to them. -
Alternatively, if you really want to handle in a specific behavior all your inputs, and forget that view exists:
-
As for watching “what is pressed”: A behavior can access the container as simply as
Application.MainWindow.Container
. You can use this to watch what is pressed in a behavior’sUpdate
method (or from anywhere else).
-
As for press/release: While a behavior doesn’t get notified about press / release of things (only
TCastleUserInterface
descendants, includingTCastleView
, have them), you could add it. Define in your behavior methods like
type TMyBehavior = class(TCastleBehavior) function Press(const Event: TInputPressRelease): boolean; function Release(const Event: TInputPressRelease): boolean;
and then just call them from your view:
function TViewMain.Press(const Event: TInputPressRelease): Boolean; begin Result := inherited; if Result then Exit; // allow the ancestor to handle keys Result := MyBehaviorThatShouldProcessAllInput.Press(Event); end;
-
Hm, actually that’s it In the end, all my 3 answers above seem similar, they are a variation of “handle input in a view, and pass it to your behavior somehow”
Hope this helps.
Note: The TInputShortcut
and CastleInputs
unit, used by navigation components, shown also in examples/viewport_and_scenes/custom_input_shortcuts_saved_to_config
, are an addition to allow to have configurable inputs (e.g. to allow player to “customize key-bindings” and save them to config file). You’re welcome to look at them, though maybe they don’t directly answer your question (“how to handle input in a behavior”).
Note 2: The larger question I guess is “why do you want to handle input in a behavior”, i.e. what is the ultimate thing you want to do. If our above answers didn’t help (or even if they did!) feel welcome to describe your use-case in more detail, like “I want users to be able to do X, in a game like Y, and I use behaviors for Z”. This may allow us to advise even better
Hi, the claryfication indeed helped a lot. My little bunny is now moving on screen as intended. I’m not using a behavior anymore but the idea was (before I even started out) to use behaviors as some kind of substitute state machine. Since games often have to use the same buttons for various actions depending on the state of the game (e.g. wasd do normal walking in “normal walk” mode, but do slightly different walking in “focused on enemy” mode) I thought I might do different behaviors for these states with the fitting input controls and then turn one off, if the game goes into the other state and vice versa.
normal walk state → normal walk behavior on; focused on enemy walk behavior off.
focused walk state → normal walk behavior off; focused on enemy walk behavior on.
I’m aware there are propably tons of ways one could implement this without such behaviors, and also that this has to be clean to not turn into a logic mess, but that was one idea i had, which is why I wanted to start out with behaviors.
Thanks a lot for all the answers!
By the way:
I’m now struggling with rotation instead. Does anybody have tips on how to rotate a character using mouse movement. I’m trying to use the MouseLookDelta and it somehow works, but I’m definitely doing something wrong because my bunny makes weird snaps and my mouse cursor does not get centered…
Thanks again!
When using “mouse look” and the MouseLookDelta
, it a deliberate feature that we capture mouse cursor, hide it (you should not even care where is the mouse cursor) and expose just “mouse delta”. Experiment with examples/user_interface/dragging_test/
to see how it’s used, in 3D it’s used typically through TCastleWalkNavigation.MouseLook
.
If you want to just handle mouse being moved to do something, override Motion
event of a view, and compare previous and new mouse position to do anything, like
function TViewMain.Motion(const Event: TInputMotion): Boolean;
var
XDrag: Single;
begin
Result := inherited;
if Result then Exit; // allow the ancestor to handle event
if buttonLeft in Event.Pressed then
begin
XDrag := Event.Position.X - Event.OldPosition.X;
// do anything you want with XDrag, e.g. rotate something around Y axis
MyScene.Rotation := Vector4(0, 1, 0, MyScene.Rotation.W + XDrag);
Exit(true);
end;
end;
My above example is probably silly Just wanted to show how to handle “mouse move” in the simplest way, and use it to apply some rotation (assuming
MyScene
is always rotated around Y axis) in some way. Hope this helps