ID:1927632
 
(See the best response by Ter13.)
how to make it so i can double tap a button to perform a alternative action and how to make an alternative action when two buttons are held. Example walking diagonally when north and east are held

http://www.byond.com/developer/?text=tag%3amovement , I believe there are few good libs here you can find about this subject.
I recommend u look at ter's, FA's is pretty inefficient
In response to Zagros5000
looking at ters honestly that should be built into byond, why the default setup of touching keys in a game engine is so advance is beyond my comprehension. This should literally be the easiest thing to do like every other game engine which usually is 1 line of code.

Its why we have so many games with no controls at all and just verb panels
I have a hidden library that lets me write code like this:
mob
Login()
..()
client.EnableKeyboard()
client.BindAxis("x", "d", "a")
client.BindAxis("y", "w", "s")
client.BindKey("z")
client.BindKey("x")

// Events will fire for the a, d, s, w, x, z keys
KeyDown(Key)
src << "Pressed [Key]"

DblTap(Key)
src << "Double-tapped [Key]"

// Other events: KeyUp, Tap, KeyRepeat

// Multi-key movement with WASD
// This event happens every frame while any key is down
KeyTick()
var ax = client.GetAxisState("x")
var ay = client.GetAxisState("y")
ax && step(src, ax > 0 ? EAST : WEST)
ay && step(src, ay > 0 ? NORTH : SOUTH)

The code that makes this work is less than 150 lines.

Even though it took me less than an hour to write, I never have to write that code again. It might as well be part of the engine. I don't even have to look at it.

It would be "nice" for a game engine to support this natively, but now that I've so easily written it, I don't even need it to be supported natively.

But the nice thing is, since I have access to its source code, I can modify or extend it if I want to.

You can't do that with the BYOND engine.
In response to Kaiochao
yes i realize you can make functions to make your life easier in programming languages. What I don't realize is why dream makers default setup is so bad to the point it doesn't make sense. Obviously I am well versed in basic controls, but I still find the way to set up basic controls nonsensical. Sorry just me ranting, It's the one thing I've probably disliked the most.

Like i said the proof of this is in the number of people who make games without any button controls. Buttons should not be an intermediate level to set up. It should be the most basic thing. And like unity take 1 line instead of 150
In response to DanteVFenris
If you make a library for the kind of movement control system you want, then any project that uses it will have taken you 0 lines to include it, instead of 1 or 150. Just download the library and check the box and you're good to go.

Also, I've used Unity. It takes a lot more than "writing one line" to move a GameObject with WASD or arrow keys.
1. Click your GameObject.
2. Add a Rigidbody2d to your GameObject. Maybe a 2D collider component too.
3. Add a New Script to your GameObject for player movement.
4. Get that Rigidbody2d into your player movement script with GetComponent<>().
5. In Update(), Get the "Horizontal" and "Vertical" axis values using Input.GetAxis() or Input.GetAxisRaw(). If you don't want to be using WASD or the arrow keys, you have to change this in the Input Manager.
6. Apply force to the Rigidbody2d according to the input vector.

The DM equivalent of setting up the Input Manager's controls, using my keyboard handling library:
mob
Login()
..()
client.EnableKeyboard()
client.BindAxis("Horizontal", "d", "a")
client.BindAxis("Vertical", "w", "s")

The DM equivalent of that player movement script:
mob
KeyTick()
var ax = client.GetAxisState("Horizontal") // DM-equivalent of Input.GetAxisRaw()
var ay = client.GetAxisState("Vertical")
ax && step(src, ax > 0 ? EAST : WEST) // DM-equivalent of translating the rigidbody
ay && step(src, ay > 0 ? NORTH : SOUTH)
Best response
#define TICK_LAG 0.25 //set this to 10 divided by world.FPS

//this is where you would define your binds. Simply define them as numbers starting at 1, then update the BindKey() verb as necessary to handle behavior.
#define BIND_JUMP 1
#define BIND_INVENTORY 2

#define TAP_THRESHOLD 2.5

client
var/tmp
move_keys = 0 //stores the actual state of the movement keys
move_dir = 0 //stores the direction the client is trying to move the mob in
move_loop = 0 //stores the current move_loop's starting time

last_key = 0 //stores the last key that was actually pressed
//these lists must be as long as 4+ the number of binds you define.
list/key_state = list(0,0,0,0,0,0) //stores the on-off state of each registered key
list/key_time = list(-1#INF,-1#INF,-1#INF,-1#INF,-1#INF,-1#INF) //stores the time that the registered key was pressed or released
list/key_taps = list(0,0,0,0,0,0) //stores the number of times a key has been pressed in sequence. This allows for dealing with double and triple taps.
proc
//this drives movement behavior.
MoveLoop()
if(move_loop) return //only call once while active
var/x,y
move_loop = world.time
//repeat while move_keys are being held down
var/turf/t
while(move_keys)
if(move_dir)
t = get_step(mob,move_dir)
if(t)
Move(t,move_dir)
sleep(TICK_LAG)
move_loop = 0
verb
//called when a movement key has been pressed.
MoveKey(dir as num,state as num)
set hidden = 1
var/opp = turn(dir,180)
var/pos = log(2,dir)+1
if(state&&++key_state[pos]==1)
//key track of the keytap state
if(world.time-key_time[pos]>TAP_THRESHOLD)
key_taps[pos] = 1
else if(last_key==pos)
++key_taps[pos]
else
key_taps[pos] = 1
last_key = pos

//keep track of the move keys and direction
move_keys |= dir
if(move_keys&opp)
move_dir &= ~opp
else
move_dir |= dir
//store the time of the event
key_time[pos] = world.time
//this is an example of handling double-taps for movement
/*if(move_keys==dir&&key_taps[pos]==2)
mob.Dash(dir,0,pos)*/

else if(!state&&--key_state[pos]==0)
//this is a key release
move_keys &= ~dir
if(move_keys&opp)
move_dir |= opp
else
move_dir &= ~dir
//store the time of the event
key_time[pos] = world.time
//attempt to call MoveLoop if there are keys being held down
if(move_keys)
MoveLoop()

//called when a registered bind has been pressed
BindKey(key as num,state as num)
set hidden = 1
var/pos = key+4
key_state[pos] = state
//this is a keypress
if(state)
last_key = pos
//keep track of double, triple, quadruple, etc taps
if(world.time-key_time[pos]>TAP_THRESHOLD)
key_taps[pos] = 1
else
++key_taps[pos]
switch(key)
//these are just examples, use your game's binds and functions here.
if(BIND_JUMP)
if(state)
switch(move_dir&(EAST|WEST))
if(EAST)
mob.Jump(60)
if(WEST)
mob.Jump(120)
else
mob.Jump(90)
if(BIND_INVENTORY)
if(state)
if(interface.inventory.showing)
interface.inventory.Hide()
interface.equipment.Hide()
else
interface.inventory.Show()
interface.equipment.Show()

North()
South()
East()
West()
Northeast()
Southeast()
Southwest()
Northwest()


This is how you define macros:
W        MoveKey 1 1
W+UP MoveKey 1 0
S MoveKey 2 1
S+UP MoveKey 2 0
D MoveKey 4 1
D+UP MoveKey 4 0
A MoveKey 8 1
A+UP MoveKey 8 0

SPACE BindKey 1 1
SPACE+UP BindKey 1 0

I BindKey 2 1
I+UP BindKey 2 0


This setup will keep track of several things for you:

1) The number of taps in a sequence. (client.key_taps)
2) The time that the key was last pressed/released (client.key_time)
3) Whether a key is currently held down. (client.key_state)

client.MoveKey() and client.BindKey() is where most of the magic happens. By default, it modifies the current state of the key lists, then the functions are all about being overridden and changed.

Pay close attention to the lists in the client variables. If you add bindkeys, you need to change the length of the list to match the maximum number of keys that it can track.
In response to Kaiochao
Kaiochao wrote:
If you make a library for the kind of movement control system you want, then any project that uses it will have taken you 0 lines to include it, instead of 1 or 150. Just download the library and check the box and you're good to go.

Also, I've used Unity. It takes a lot more than "writing one line" to move a GameObject with WASD or arrow keys.
1. Click your GameObject.
2. Add a Rigidbody2d to your GameObject. Maybe a 2D collider component too.
3. Add a New Script to your GameObject for player movement.
4. Get that Rigidbody2d into your player movement script with GetComponent<>().
5. In Update(), Get the "Horizontal" and "Vertical" axis values using Input.GetAxis() or Input.GetAxisRaw(). If you don't want to be using WASD or the arrow keys, you have to change this in the Input Manager.
6. Apply force to the Rigidbody2d according to the input vector.

The DM equivalent of setting up the Input Manager's controls, using my keyboard handling library:
> mob
> Login()
> ..()
> client.EnableKeyboard()
> client.BindAxis("Horizontal", "d", "a")
> client.BindAxis("Vertical", "w", "s")
>

The DM equivalent of that player movement script:
> mob
> KeyTick()
> var ax = client.GetAxisState("Horizontal") // DM-equivalent of Input.GetAxisRaw()
> var ay = client.GetAxisState("Vertical")
> ax && step(src, ax > 0 ? EAST : WEST) // DM-equivalent of translating the rigidbody
> ay && step(src, ay > 0 ? NORTH : SOUTH)
>

wrote up a quick movement system in unity its really more simple than byond movement because be defualt movement is just so smooth.
using System.Collections;

public class Movement : MonoBehaviour {
public float speed = 1f;
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
if (Input.GetKey(KeyCode.D)) // move right.
transform.position += new Vector3 (speed * Time.deltaTime, 0.0f , 0.0f);

if (Input.GetKey(KeyCode.A)) // move left.
transform.position -= new Vector3 (speed * Time.deltaTime, 0.0f , 0.0f);

if (Input.GetKey(KeyCode.W)) // move up.
transform.position += new Vector3 (0.0f , speed * Time.deltaTime , 0.0f);

if (Input.GetKey(KeyCode.S)) // move down
transform.position -= new Vector3 (0.0f , speed * Time.deltaTime , 0.0f);
}
}

In response to Hebrons
DM (60fps, recorded at 30fps):

Looks smooth to me.

Entity loop, a basic equivalent of Unity's GameObjects with components that update periodically, analogous to... using Unity:
world
fps = 60
mob = /entity

New()
..()
spawn EntityLoop()

proc/EntityLoop() for()
for(var/entity/e)
e.Update()
sleep tick_lag

entity
parent_type = /mob

var components[0]

proc/Update()
for(var/component/c in components)
c.Update()

proc/AddComponent(component/C)
components += C
C.entity = src

component
var entity/entity
proc/Update()


Adding a component to a player, analogous to adding a MonoBehavior to a player GameObject:
entity/Login()
..()
e.AddComponent(new /component/player_movement)


Movement component, analogous to your Movement MonoBehavior:
component/player_movement
var speed = 1 // 60 pixels per second

// Update is called once per frame
Update()
if(entity.client.GetKeyState("d")) // move right.
step(entity, EAST, speed)

if(entity.client.GetKeyState("a")) // move left.
step(entity, WEST, speed)

if(entity.client.GetKeyState("w")) // move up.
step(entity, NORTH, speed)

if(entity.client.GetKeyState("s")) // move down.
step(entity, SOUTH, speed)



But actually, in Unity, you could also do it like this:
void Update() {
transform.Translate(Time.deltaTime * speed * Input.GetAxisRaw("Horizontal") * Vector2.right);
transform.Translate(Time.deltaTime * speed * Input.GetAxisRaw("Vertical") * Vector2.up);
}


In DM, I've done that like this:
Update()
entity.Translate(speed * entity.client.CheckAxis("x"), 0)
entity.Translate(0, speed * entity.client.CheckAxis("y"))
can we incorporate C# in BYOND games or only JS & HTML ?
No dice on C#.
lol ^
To be fair, you aren't really using C# in Unity either. You are using MonoC# --different animal.
In response to Ter13
Ter13 wrote:
To be fair, you aren't really using C# in Unity either. You are using MonoC# --different animal.

i know lol just curious.
In response to Ter13

This is how you define macros:
> W        MoveKey 1 1
> W+UP MoveKey 1 0
> S MoveKey 2 1
> S+UP MoveKey 2 0
> D MoveKey 4 1
> D+UP MoveKey 4 0
> A MoveKey 8 1
> A+UP MoveKey 8 0
>
> SPACE BindKey 1 1
> SPACE+UP BindKey 1 0
>
> I BindKey 2 1
> I+UP BindKey 2 0
>


i dont get this part. Where does this even go. Just get erros when i copy and paste. I assume this is to set up controls but ive never come across this.

i dont get this part. Where does this even go. Just get erros when i copy and paste. I assume this is to set up controls but ive never come across this.

These are the settings for your macros in the DMF file.
In response to Ter13
i know this is old but anyway you can think of a way to modify it to be pixel based. Tile based is way way too fast.I know i can change the lag time but that makes it kinda look ugly as im sure you know.

I tried using a couple scenarios but all of them would break your idea. Maybe i just dont understand something well enough. I know where i need to modify, just can't think of a way to modify it to allow it
Tiled movement fix:

Tile based is way way too fast.

You don't want to slow down the loop. You want the loop to force the player to try to move every frame. Modify movables to track their own movement speed. The client should not need to know any of this information to drive a movable.

I know i can change the lag time but that makes it kinda look ugly as im sure you know.

Change the glide size to mitigate the ugliness.

#define MOVE_TURN     1
#define MOVE_SLIDE 2
#define MOVE_JUMP 4
#define MOVE_TELEPORT 8

#define TILE_WIDTH 32
#define TILE_HEIGHT 32
#define TILE_DIAG sqrt(TILE_WIDTH * TILE_WIDTH + TILE_HEIGHT * TILE_HEIGHT)

atom/movable
var
tmp
next_move = 0
move_type = 0
move_delay = TICK_LAG
glide_delay = TICK_LAG

Move(atom/NewLoc,Dir=0,Step_x=0,Step_y=0,Source=null)
if(!Source) Source = src
var/mtime = world.time
//make sure that we're not moving before we're allowed
if(next_move>mtime&&Source==src)
return 0
//this bit sets what kind of move we just tried to do and manages the glide size
if(loc&&NewLoc.z==z)
var/mx = abs(NewLoc.x - x)
var/my = abs(NewLoc.y - y)
move_type = MOVE_SLIDE
//determine if jump, turn or slide.
if(mx>1||my>1)
//more than one tile at a time movement is a jump
move_type = MOVE_JUMP
else if(mx)
//set the glide size based on the movement direction
if(my)
glide_size = TILE_DIAG / glide_delay / TICK_LAG
else
glide_size = TILE_WIDTH / glide_delay / TICK_LAG
else if(my)
glide_size = TILE_HEIGHT / glide_delay / TICK_LAG
else
//no movement is a turn
move_type = MOVE_TURN
else
//swapping z layers is a teleport
move_type = MOVE_TELEPORT
. = ..()
//set the next movement time
if(Source==src)
next_move = mtime + move_delay


Pixel movement fix:

As for changing it over to pixel movement, the default client Move() function sucks for this. It's designed for tiled movement, and has always sucked for pixel movement overrides.

Let's just write a better hook for handling it

client
proc
//provide a hook override so you can do things like reversing the client's controls
DirMove(Dir)
var/sx = mob.step_x
var/sy = mob.step_y
if(Dir&EAST)
sx += mob.step_size
if(Dir&WEST)
sx -= mob.step_size
if(Dir&SOUTH)
sy -= mob.step_size
if(Dir&NORTH)
sy += mob.step_size
return mob&&mob.loc&&mob.Move(mob.loc,Dir,sx,sy,mob)


Now, we just modify the MoveLoop function:

    proc
//this drives movement behavior.
MoveLoop()
if(move_loop) return //only call once while active
var/x,y
move_loop = world.time
//repeat while move_keys are being held down
while(move_keys)
if(move_dir)
DirMove(move_dir)
sleep(TICK_LAG)
move_loop = 0


Boom, pixel movement compatible.
In response to Ter13
What if you did this:
client
proc
DirMove(Dir) switch(Dir)
if(NORTH) return North()
if(SOUTH) return South()
if(EAST) return East()
if(WEST) return West()
if(NORTHEAST) return Northeast()
if(SOUTHEAST) return Southeast()
if(NORTHWEST) return Northwest()
if(SOUTHWEST) return Southwest()
Page: 1 2