How to tell when a RepeatButton is done being Pressed

I want to make movement using on-screen buttons that you can hold down, unfortunately while the keyboard inputs work as intended, the on-screen inputs just won’t. They move the controlled object way too fast for some reason when I try to use those “else” statements in “On GUI” and if I try to use booleans to control it, the same thing happens. If I remove or comment out the else statements in OnGUI I get normal movement, albeit not what I intend (I want it to snap to integers after the controls are let go of). So… any ideas as to why this is happening and how to fix it?

#pragma strict

var speed : float = 3;
var int_position : int;
var negative_int_position : int;

var move_left : boolean;
var move_right : boolean;

var on_screen_only : boolean;
var keyboard : boolean;
var keys_or_screen : String;

function Start () {

}

function Update () {

if(keyboard == true)
{
	if(Input.GetKey(KeyCode.D))
	{
	transform.position.x += speed * Time.deltaTime;
	}
	else if(Input.GetKeyUp(KeyCode.D))
	{
	int_position = Mathf.CeilToInt(transform.position.x);
		
		if(transform.position.x != int_position)// && transform.position.x > 0)
		{
		transform.position.x = int_position;
		}
	}
	
	if(Input.GetKey(KeyCode.A))
	{
	transform.position.x -= speed * Time.deltaTime;
	}
	else if(Input.GetKeyUp(KeyCode.A))
	{
	negative_int_position = Mathf.FloorToInt(transform.position.x);
		
		if(transform.position.x != negative_int_position)
		{
		transform.position.x = negative_int_position;
		}
	}
}

}

function OnGUI () {
if(GUI.Button(new Rect(0,50,100,50),"On-Screen"))
{
on_screen_only = true;
keyboard = false;
keys_or_screen = "On-Screen";
}
if(GUI.Button(new Rect(100,50,100,50),"Keyboard"))
{
on_screen_only = false;
keyboard = true;
keys_or_screen = "Keyboard";
}
GUI.Box(new Rect(200,50,100,50),keys_or_screen);

if(on_screen_only == true)
{
	if(GUI.RepeatButton(new Rect(0,Screen.height - 50,100,50),"<-"))
	{
	//move_left = true;
	transform.position.x -= speed * Time.deltaTime;
	}
	else
	{
	//move_left = false;
	negative_int_position = Mathf.FloorToInt(transform.position.x);
		if(transform.position.x != negative_int_position)
		{
		transform.position.x = negative_int_position;
		}
	
	
	}

	if(GUI.RepeatButton(new Rect(100,Screen.height - 50,100,50),"->"))
	{
	transform.position.x += speed * Time.deltaTime;
	}
	/*else
	{
	int_position = Mathf.CeilToInt(transform.position.x);
		if(transform.position.x != int_position)// && transform.position.x > 0)
		{
		transform.position.x = int_position;
		}
	}*/
}

}

Try OnMouseDown & OnMouseUp function

function OnMouseDown()
{
transform.position.x += speed * Time.deltaTime;
}
function OnMouseUp()
{
int_position = Mathf.CeilToInt(transform.position.x);
 
        if(transform.position.x != int_position)// && transform.position.x > 0)
        {
        transform.position.x = int_position;
}

I think I can point out some of the causes for trouble in your code.

First, I believe the reason why commenting out one of the else statements allows the object to move is because both else statements are called every OnGUI update. The negative_int position is applied first, then the regular int_position is applied right after, canceling out any movement.

Another trouble I noticed was that if a Debug.Log is placed inside the “else” of the GUI.RepeatButton, it is displayed even when the button is being pressed. I found a link where somebody posted a solution for the issue of making sure the GUI.RepeatButton is actually released.

I also think that part of the trouble for the speed discrepancy between using the keyboard and the on-screen buttons is because OnGUI() is called more often than Update(). More info in this link:

My suggested solution to this is to keep with the idea of the booleans in the GUI.RepeatButton and move the actual movement code into the Update() function.

Something inside the Update() function along the lines of:

	if( move_left ){
		transform.position.x -= speed * Time.deltaTime;
	} else if (left_button_released) {
    	negative_int_position = Mathf.FloorToInt(transform.position.x);
        if(transform.position.x != negative_int_position){
        	transform.position.x = negative_int_position;
        }
        
        left_button_released = false;
	}
	
	if( move_right ){
		transform.position.x += speed * Time.deltaTime;
	} else if (right_button_released) {
    	int_position = Mathf.CeilToInt(transform.position.x);
        if(transform.position.x != int_position){
        	transform.position.x = int_position;
        }
        
        right_button_released = false;
	}

This code is untested, and the button_released booleans would want to be set true in the case of an actual release, referring to the first link I posted above about detecting the true release of a GUI.RepeatButton.

EDIT:

Here is a complete edit of your script, including corrections for the troubles I mentioned above.

#pragma strict
 
var speed : float = 3;
var int_position : int;
var negative_int_position : int;
 
var move_left : boolean;
var move_right : boolean;

var left_button_released : boolean;
var right_button_released : boolean;
 
var on_screen_only : boolean;
var keyboard : boolean;
var keys_or_screen : String;
 
function Start () {
 
}
 
function FixedUpdate () {
 
	if(keyboard == true){
	    if(Input.GetKey(KeyCode.D)){
	    	transform.position.x += speed * Time.deltaTime;
	    }
	    else if(Input.GetKeyUp(KeyCode.D)){
	    	int_position = Mathf.CeilToInt(transform.position.x);
	 
	        if(transform.position.x != int_position){
	        	transform.position.x = int_position;
	        }
	    }
	 
	    if(Input.GetKey(KeyCode.A)){
	    	transform.position.x -= speed * Time.deltaTime;
	    } else if(Input.GetKeyUp(KeyCode.A)){
	    
	    	negative_int_position = Mathf.FloorToInt(transform.position.x);
	 
	        if(transform.position.x != negative_int_position){
	        	transform.position.x = negative_int_position;
	        }
	    }
	}
	

	
	if( move_left && !left_button_released ){
		transform.position.x -= speed * Time.deltaTime;

	} else if( move_left && left_button_released ) {
    	negative_int_position = Mathf.FloorToInt(transform.position.x);
        if(transform.position.x != negative_int_position){
        	transform.position.x = negative_int_position;
        }
        move_left = false;
	}
	
	if( move_right && !right_button_released ){
		transform.position.x += speed * Time.deltaTime;

		
	} else if (move_right && right_button_released){
    	int_position = Mathf.CeilToInt(transform.position.x);
        if(transform.position.x != int_position){
        	transform.position.x = int_position;
        }
        move_right = false;
	}
	
	left_button_released = true;
	right_button_released = true;
 
}

//function OnMouseUp(){
//
//	left_button_released = true;
//	right_button_released = true;
//		
//}
 
function OnGUI () {
	if(GUI.Button(new Rect(0,50,100,50),"On-Screen"))
	{
		on_screen_only = true;
		keyboard = false;
		keys_or_screen = "On-Screen";
	}
	if(GUI.Button(new Rect(100,50,100,50),"Keyboard"))
	{
		on_screen_only = false;
		keyboard = true;
		keys_or_screen = "Keyboard";
	}
	GUI.Box(new Rect(200,50,100,50),keys_or_screen);
	 
	if(on_screen_only == true){
	    if(GUI.RepeatButton(new Rect(0,Screen.height - 50,100,50),"<-")){
	    	move_left = true;
	    	left_button_released = false;
	    } 
	 
	    if(GUI.RepeatButton(new Rect(100,Screen.height - 50,100,50),"->")){
	    	move_right = true;
	    	right_button_released = false;
	    } 

	}
}

This fixes the multiple update issue of having the movement code inside of the OnGUI() function. The movement code is now only called once per frame.

This also corrects for the conflicting logic of your original code.

First suggestion: Abandon OnGUI and use the 4.6 beta

Second suggestion: Watch this 1 from Unite 2013 to get the hang of how the intermediate mode GUI works

Third suggestion: Read the rest of my answer, I’ll try explain it here.

First thing to understand is that OnGUI gets called multiple times per frame. The code will run once per GUI event. So it runs once for a mouse button down, once for a mouse button up, once for a key down and so forth. On top of that it runs a layout pass before calling each event, this is where it gets all of the rects and positions everything on the screen. There are also redraw events.

There are a couple side effects of this multiple call scenario. First is OnGUI is slow. Every GUI element is processed multiple times. Even if it just sitting there. That’s why other solutions are popular in published games

Second up is that code that is not part of an if loop runs multiple times. This means anything on its own, or in an else clause will run at least once. Maybe more times. GUI buttons get around this by internally checking which event is being run through. GUIButton will only return true if the event is MouseDown, and the mouse is inside the bounds of the button rect. Everything else, including used events and layout events are ignored. You need to programme this behaviour into your else clause.

Now for some actual pseudo code. Be aware I haven’t touched OnGUI since the 4.6 was released, it might not compile first time around.

private bool isButtonDown;

void OnGUI (){
    if (GUI.RepeatButton(....)){
        isButtonDown = true;
    } else if (isButtonDown && Event.current == EventType.MouseUp){
        isButtonDown = false;
    }
}

That should be enough to get you started. Let me know if you need more assistance.