[Solved] Wobbly mouse pointer for 'inaccurate aiming' and recoil in horror game

Started by Lewis, Tue 15/09/2020 18:32:38

Previous topic - Next topic

Lewis

I've been trying to fathom this one on my own but it turns out I really have no idea where to start.

For context, I've been working on a survival horror style game in AGS for a little while now. One thing that I'd like to incorporate is a sort of 'wobbly aiming' mechanic whereby when the player is aiming the reticule at an enemy, the cursor/reticule sort of wobbles around the point where the mouse is on screen as if your aiming is imperfect (for example, like with the sniper rifle in the Deus Ex games - watch from 13 secs:) https://youtu.be/W78OSUJZrBU

I'm also hoping to add a recoil effect whereby when the player shoots the enemy, the reticule/cursor snaps upward on the y axis.

The aiming/firing mechanic simply uses an inventory item (iPistol) which, while active, changes the mouse pointer to an aiming reticule. Clicking this on the enemy (cMonster) then triggers a bunch of script that isn't especially relevant here.

Can anyone give me some pointers (pun very much intended) as to how to approach this?
Returning to AGS after a hiatus. Co-director of Richard & Alice and The Charnel House Trilogy.

Cassiebsg

I did something like that for a game of mine, though mine was an bow and arrow.

Basically what I did, was in rep_exec_always added a small random value to the mouse position.
You can check it here if this is more or like what you're looking for: https://www.adventuregamestudio.co.uk/site/games/game/2215-stable-pete-and-the-joust-demo-/ (go to the archery contest area on the middle left.)
There are those who believe that life here began out there...

Lewis

Yeah, that's pretty much it! I'd prefer a smoother movement but I'd settle for this. I'll have a play around with that method, thanks!
Returning to AGS after a hiatus. Co-director of Richard & Alice and The Charnel House Trilogy.

Cassiebsg

This is my code for the room. Hope it can give you some ideas on how to code yours.
I think all the code is contained in here, but there might be some global variables and code in Global Script... but I need to go to bed and have no time to check or clean up the "useless" code.

Code: ags


// room script file

int PreviousMouseX;
int PreviousMouseY;
int intTruncate;
int TimerBow;
int MouseX;
int MouseY;
bool IsArrowLocked;
bool IsArrowOnTarget;
int intArrowTries;

function room_Load()
{
	aRPG_Battle_Climax_2.Play(eAudioPriorityHigh, eRepeat);
	intTruncate=0;
	lbPower.Text=lbPower.Text.Truncate(intTruncate);
	lbPower.TextColor=63488;
	gPower.Visible=true;
	gScore.Visible=true;
	gAtempts.Visible=true;
	gActionText.Visible=false;
	lbScore.Text="0";
	intArrowTries=0;
	IsArrowLocked=false;
	IsArrowOnTarget=false;
	TimerBow=0;
	intTruncate=0;
	gAnouncer.Visible=true;
	
}


function room_Leave()
{
	gAnouncer.Visible=false;
	Wait(1);
	player.ChangeRoom(1);
}



function ScoringArchery()
{
	if (mouse.IsButtonDown(eMouseRight))  return;
	else if (intArrowTries<4 && IsArrowOnTarget)
	{
		if (Hotspot.GetAtScreenXY(oPoint.X, oPoint.Y)== hotspot[1]) // Arrow has hit the target1
		{
			intScoreArchery=intScoreArchery+1;
		}
		else if (Hotspot.GetAtScreenXY(oPoint.X, oPoint.Y)== hotspot[2]) // Arrow has hit the target2
		{
			intScoreArchery=intScoreArchery+2;
		}
		else if (Hotspot.GetAtScreenXY(oPoint.X, oPoint.Y)== hotspot[3]) // Arrow has hit the target3
		{
			intScoreArchery=intScoreArchery+3;
		}
		else if (Hotspot.GetAtScreenXY(oPoint.X, oPoint.Y)== hotspot[4]) // Arrow has hit the target4
		{
			intScoreArchery=intScoreArchery+4;
		}
		else if (Hotspot.GetAtScreenXY(oPoint.X, oPoint.Y)== hotspot[5])  // Arrow has hit the target5
		{
			intScoreArchery=intScoreArchery+5;
		}
		else player.SayBackground("Miss!");
	}
	lbScore.Text=String.Format("%d" , intScoreArchery);
	intTruncate=0;
	lbPower.Text=lbPower.Text.Truncate(intTruncate);
	lbPower.TextColor=63488;
}

function on_mouse_click (MouseButton button) 
{
		if (mouse.Mode==eModeArchy && button==eMouseLeft)
		{
			if (IsArrowLocked)
			{
				if (IsArrowLocked && intTruncate<1) player.SayBackground("I need to power the bow first.");
				else
				{					
					intArrowTries++;	
					if (IsArrowLocked) // Fire the Arrow code
					{
						IsArrowLocked=false;
						oArrow.Baseline=9;
						oPoint.Baseline=8;
						if (IsArchWonAble)
						{
							oArrow.Move(300, 182,  intTruncate+1,  eBlock,  eAnywhere); //(oArrow.X+PreviousMouseX/8, oArrow.Y-PreviousMouseY/8, intTruncate+1, eBlock, eAnywhere);
						}
						else // Player can't win
						{	
							if (intTruncate>18) 
							{
								if (game.debug_mode) player.SayBackground("");
								PreviousMouseY=PreviousMouseY-100;
							}
							else if (intTruncate<16) 
							{
								if (game.debug_mode) player.SayBackground("");				
								PreviousMouseY=PreviousMouseY+100;
							}
							else  if (game.debug_mode) player.SayBackground("");
							oArrow.Move(PreviousMouseX+6, PreviousMouseY-6, intTruncate+1, eBlock, eAnywhere); // Arrow flyes!
							IsArrowOnTarget=true;		
						}
						ScoringArchery();
					}
					if (intArrowTries==1) btnTry1.NormalGraphic=40;
					else if (intArrowTries==2) btnTry2.NormalGraphic=40;
					else if (intArrowTries==3) btnTry3.NormalGraphic=40;
					if (intArrowTries<3)
					{
						player.Say("Reload!");
						Wait(40);
						IsArrowOnTarget=false;
					}
					else  
					{
						Wait(5);
						room_Leave();
					}
				}
			}	
			else 
			{
				player.SayBackground("");
			}
		}
}
	
function UpdateBOW()
{
	if (mouse.IsButtonDown(eMouseRight))
	{
		IsArrowLocked=true;
		lbPower.TextColor=63488;
		if (PreviousMouseX>mouse.x)
		{
			if (lbPower.Text=="")  player.SayBackground("");
			else 
			{
				lbPower.Text=lbPower.Text.Truncate(intTruncate);
				if (intTruncate>0) intTruncate--;
			}
		}
		else if  (PreviousMouseX<mouse.x)
		{
			if (lbPower.Text=="llllllllllllllllllllll") player.SayBackground("");
			else 
			{
				lbPower.Text=lbPower.Text.Append("l");
				if (intTruncate<22) intTruncate++;
			}
		}
		PreviousMouseX=mouse.x;	
		PreviousMouseY=mouse.y;	
		if (TimerBow>=6) // adjust arm shake
		{
			int ran=Random(4);
			if (ran==0) MouseX=MouseX-2;
			else if (ran==1) MouseX=MouseX+2;
			else if (ran==2) MouseX=MouseX+2;
			else if (ran==3) MouseX=MouseX-2;
			else return;	
				
			oBow.Move(MouseX, MouseY, 10, eNoBlock, eAnywhere); // oBow in room2
			if (!IsArrowOnTarget) 
			{
				oArrow.Move(MouseX+8, MouseY-144, 10, eNoBlock, eAnywhere); // oArrow in room2
			}
			TimerBow=0;
		}
		else TimerBow++;
	}
	else
	{		
		if (intTruncate>0) lbPower.TextColor=2016;
		MouseX=mouse.x;
		MouseY=mouse.y+152; // sprite half height
		if (!IsArrowOnTarget)
		{
			if (TimerBow>=8)
			{
				if (MouseX>322) // center 300, 182
				{
					MouseX=MouseX+4;
				}
				else if (MouseX<282)
				{
					MouseX=MouseX-4;
				}
				else 
				{
					int ran=Random(9);
					if (ran==0) MouseX=MouseX-4;
					else if (ran==1) MouseX=MouseX+4;
					else if (ran==2) MouseX=MouseX+6;
					else if (ran==3) MouseX=MouseX-6;
					else if (ran==4) MouseY=MouseY-4;
					else if (ran==5) MouseY=MouseY+4;
					else if (ran==6) MouseY=MouseY+6;
					else if (ran==7) MouseY=MouseY-6;
					else return;	
				}
				oBow.Move(MouseX, MouseY, 10, eNoBlock, eAnywhere); // oBow in room2
				if (!IsArrowOnTarget) oArrow.Move(MouseX+8, MouseY-144, 10, eNoBlock, eAnywhere); // oArrow in room2
				TimerBow=0;
			}
			else TimerBow++;
		}
	}
}



function room_RepExec() 
{
	if (intArrowTries>=3 || mouse.x>=440 || mouse.x<=180 || mouse.y>=275 || mouse.y<=75) mouse.Mode=eModeWalkto;
	else 
	{
		mouse.Mode=eModeArchy;
	}	
	if (intArrowTries<3) UpdateBOW();
	
}



function late_repeatedly_execute_always()
{
	if (intArrowTries<3)
	{
		oPoint.X=oArrow.X+4;
		oPoint.Y=oArrow.Y-20;
	}
}
function room_AfterFadeIn()
{
	
	if (Game.DoOnceOnly("Joust Info")) Display("Instructions[ [1st. Press and hold right mouse button to control your bow string. Move mouse right/left to adjust tension.[2nd. Release right button to lock desired tension.[3rd. Point your bow and release the arrow by clicking the left mouse button.[4th. Good luck!");
}
There are those who believe that life here began out there...

Lewis

Thanks! :)

Here's what I ended up doing. I may play around with a timer so that the mouse position doesn't necessarily update every single game loop.

Code: ags
function repeatedly_execute_always()
{
  int ran=Random(1);
  int ran2=Random(3);
  
  if (cPlayer.View==2) //This is the 'aiming' view that enables when the player selects the gun from their inventory.
  {
    if (ran2==0)
    {
      mouse.Update();
      mouse.SetPosition(mouse.x + ran, mouse.y + ran);
    }
    else if (ran2==1)
    {
      mouse.Update();
      mouse.SetPosition(mouse.x - ran, mouse.y - ran);
    }
    else if (ran2==2)
    {
      mouse.Update();
      mouse.SetPosition(mouse.x + ran, mouse.y - ran);
    }
    else
    {
      mouse.Update();
      mouse.SetPosition(mouse.x - ran, mouse.y + ran);
    }
  }
}


For the recoil, I just added this to the function where the player shoots the gun:

Code: ags
mouse.SetPosition(mouse.x, mouse.y-40);
Returning to AGS after a hiatus. Co-director of Richard & Alice and The Charnel House Trilogy.

Khris

You can use something like
Code: ags
  mouse.x += Random(2) - 1;
  mouse.y += Random(2) - 1;


This will randomly move the mouse to one of the surrounding eight pixels.

Lewis

Hi Khris! Thanks for adding your method. Can you clarify where this code would go? As mouse.position is read-only.

Cheers!
Returning to AGS after a hiatus. Co-director of Richard & Alice and The Charnel House Trilogy.

Khris

Right, here's the full version:

Code: ags
function repeatedly_execute_always() {
  if (player.View == 2) {
    mouse.SetPosition(mouse.x + Random(2) - 1, mouse.y + Random(2) - 1);
  }
}

Lewis

Returning to AGS after a hiatus. Co-director of Richard & Alice and The Charnel House Trilogy.

SMF spam blocked by CleanTalk