Crosshair: Use analog stick input properly

- Crosshair doesn't work with a dpad anymore. Supporting dpads adds
   unnecessary complexity, as a gamepad user with dpad will never be
   competitive with mouse users.

 - Crosshair isn't bound to item usage anymore, but will always show
   when the analog stick is outside of a deadzone.

Future planned feature: Also use the actual strength to allow
distance input in addition to direction. This is used heavily by
Knüppeln.c4s.
qteditor^2
Lukas Werling 2016-03-12 19:25:05 +01:00
parent 93dcb1d729
commit b8c8bf825c
3 changed files with 47 additions and 98 deletions

View File

@ -5,61 +5,69 @@
Virtual cursor for gamepad controls Virtual cursor for gamepad controls
*/ */
local crew, angle, dirx, diry, xpos,ypos, analogaim, aiming, menu; local crew, angle, xpos, ypos, aiming, menu;
static const CURSOR_Radius = 100; static const CURSOR_Radius = 100;
// This is supposed to be a constant, but C4Script doesn't allow constant expressions there.
private func CURSOR_Deadzone() { return PLRCON_MaxStrength / 5; }
protected func Initialize() protected func Initialize()
{ {
this["Visibility"] = VIS_None; SetVisibility(false);
dirx = diry = xpos = ypos = 0; xpos = ypos = 0;
aiming = false; aiming = false;
} }
public func FxMoveTimer() public func FxMoveTimer()
{ {
var speed = 0;
var dpad_rotatespeed = 35;
// dpad mode
if(diry)
{
if (diry < 0) speed = -Sin(angle,100,10);
else if (diry > 0) speed = +Sin(angle,100,10);
angle += dpad_rotatespeed*speed/100;
UpdateAnalogpadPos();
}
if(dirx)
{
if (dirx < 0) speed = -Cos(angle,100,10);
else if (dirx > 0) speed = +Cos(angle,100,10);
angle += dpad_rotatespeed*speed/100;
UpdateAnalogpadPos();
}
// analog pad mode
if(!dirx && !diry)
{
var target_angle = Angle(0,0,xpos,ypos)*10; var target_angle = Angle(0,0,xpos,ypos)*10;
var analog_strength = BoundBy(Sqrt(xpos*xpos+ypos*ypos),0,100);
if (!Visible() && !InDeadzone())
{
// The player moved the aiming stick while the crosshair wasn't visible: Use angle directly.
angle = target_angle;
SetVisibility(true);
}
else if (!InDeadzone())
{
// Smooth small movements of the stick while the crosshair is visible.
var angle_diff = Normalize(target_angle - angle, -1800, 10); var angle_diff = Normalize(target_angle - angle, -1800, 10);
if (angle_diff == 0) angle_diff = 1; if (Abs(angle_diff) < 450)
angle = angle + angle_diff / 8;
angle = angle + angle_diff * analog_strength / 100 / 8; else
angle = target_angle;
}
else if (!aiming)
{
// The player doesn't touch the stick and no item is using the crosshair right now.
SetVisibility(false);
} }
UpdatePosition(); UpdatePosition();
if(aiming) crew->TriggerHoldingControl(); crew->TriggerHoldingControl();
} }
private func UpdateAnalogpadPos() private func AnalogStrength() { return BoundBy(Sqrt(xpos*xpos+ypos*ypos), 0, PLRCON_MaxStrength); }
private func InDeadzone() { return AnalogStrength() < CURSOR_Deadzone(); }
private func Visible() { return this.Visibility != VIS_None; }
// Updates the visibility, returing true if it was changed.
private func SetVisibility(bool visible)
{ {
xpos = Sin(angle/10,100); var newvis, oldvis;
ypos = Cos(angle/10,-100); if (visible)
newvis = VIS_Owner;
else
newvis = VIS_None;
oldvis = this.Visibility;
this.Visibility = newvis;
return newvis != oldvis;
} }
public func StartAim(object clonk, bool stealth, object GUImenu) public func StartAim(object clonk, bool stealth, object GUImenu)
{ {
aiming = true;
// only reinitialize angle if the crosshair hasn't been there before // only reinitialize angle if the crosshair hasn't been there before
if(!GetEffect("Move",this)) if(!GetEffect("Move",this))
{ {
@ -80,21 +88,14 @@ public func StartAim(object clonk, bool stealth, object GUImenu)
menu = nil; menu = nil;
} }
// set starting position for analog pad // Aim somewhere useful if the crosshair wasn't visible before.
UpdateAnalogpadPos(); if (SetVisibility(true))
angle = 800*(clonk->GetDir()*2-1);
crew = clonk; crew = clonk;
UpdatePosition(); UpdatePosition();
RemoveEffect("Move",this); RemoveEffect("Move",this);
AddEffect("Move",this,1,1,this); AddEffect("Move",this,1,1,this);
if(!stealth)
{
this["Visibility"] = VIS_Owner;
crew->SetComDir(COMD_Stop);
aiming = true;
EnableKeyAimControls(true);
}
} }
private func UpdatePosition() private func UpdatePosition()
@ -112,65 +113,34 @@ private func UpdatePosition()
private func MirrorCursor() private func MirrorCursor()
{ {
return;
angle = -Normalize(angle,-1800,10); angle = -Normalize(angle,-1800,10);
UpdateAnalogpadPos();
} }
public func StopAim() public func StopAim()
{ {
RemoveEffect("Move",this);
this["Visibility"] = VIS_None;
dirx = 0;
diry = 0;
EnableKeyAimControls(false);
analogaim = false;
aiming = false; aiming = false;
} }
private func EnableKeyAimControls(bool enable)
{
SetPlayerControlEnabled(GetOwner(), CON_AimUp, enable);
SetPlayerControlEnabled(GetOwner(), CON_AimDown, enable);
SetPlayerControlEnabled(GetOwner(), CON_AimLeft, enable);
SetPlayerControlEnabled(GetOwner(), CON_AimRight, enable);
}
public func IsAiming() public func IsAiming()
{ {
return aiming; return aiming;
} }
public func Aim(int ctrl, object clonk, int strength, int repeat, int release) public func Aim(int ctrl, object clonk, int strength, int repeat, int status)
{ {
// start (stealth) aiming // start (stealth) aiming
if(!GetEffect("Move",this)) if(!GetEffect("Move",this))
StartAim(clonk,true); StartAim(clonk,true);
// aiming with analog pad // aiming with analog pad
if (ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight) if (status == CONS_Moved &&
(ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight))
{ {
dirx = diry = 0;
if(ctrl == CON_AimAxisUp) ypos = -strength; if(ctrl == CON_AimAxisUp) ypos = -strength;
if(ctrl == CON_AimAxisDown) ypos = strength; if(ctrl == CON_AimAxisDown) ypos = strength;
if(ctrl == CON_AimAxisLeft) xpos = -strength; if(ctrl == CON_AimAxisLeft) xpos = -strength;
if(ctrl == CON_AimAxisRight) xpos = strength; if(ctrl == CON_AimAxisRight) xpos = strength;
analogaim = true;
return true;
}
// stop
else if (release && !analogaim)
{
if(ctrl == CON_AimUp || ctrl == CON_AimDown) diry = 0;
else if(ctrl == CON_AimLeft || ctrl == CON_AimRight) dirx = 0;
return true;
}
else if(!release /*&& !repeat */ && !analogaim)
{
if(ctrl == CON_AimUp) diry = -1;
else if(ctrl == CON_AimDown) diry = 1;
else if(ctrl == CON_AimLeft) dirx = -1;
else if(ctrl == CON_AimRight) dirx = 1;
return true; return true;
} }
return false; return false;

View File

@ -239,8 +239,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
CON_Left is still called afterwards. So if the clonk finally starts to CON_Left is still called afterwards. So if the clonk finally starts to
aim, the virtual cursor already aims into the direction in which he ran aim, the virtual cursor already aims into the direction in which he ran
*/ */
if (ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight if (ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight)
|| ctrl == CON_AimUp || ctrl == CON_AimDown || ctrl == CON_AimLeft || ctrl == CON_AimRight)
{ {
var success = VirtualCursor()->Aim(ctrl,this,strength,repeat,status); var success = VirtualCursor()->Aim(ctrl,this,strength,repeat,status);
// in any case, CON_Aim* is called but it is only successful if the virtual cursor is aiming // in any case, CON_Aim* is called but it is only successful if the virtual cursor is aiming

View File

@ -59,26 +59,6 @@
DefaultDisabled=1 DefaultDisabled=1
CoordinateSpace=Viewport CoordinateSpace=Viewport
[ControlDef]
Identifier=AimUp
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimDown
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimLeft
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimRight
DefaultDisabled=1
Hold=1
[ControlDef] [ControlDef]
Identifier=AimAxisUp Identifier=AimAxisUp
GUIName=$CON_AimAxisUp$ GUIName=$CON_AimAxisUp$