Cycle / iterate / select near by items based on tag

Hello,

Ok so basically I understand the FindGameObjectWithTag etc… to get an array which you can then cycle through. My question is how to limit the find tag to a certain distance of the player. i.e. I don’t want an array with 800 variables or however many gameobjects happen to be in the scene then do distance checks on each one. I think the overhead on that would be totally unnecessary.

In a nutshell I’m trying to implement the tab through local selectable objects feature in FFXI and many other MMORPGs. Press tab button and a little arrow appears over the selected object in the scene, press tab again and it goes to the next near by object in the list.

Is it possible to attach say an invisible cube to the player and detect tagged item’s inside it? Or how about something like making the cube a trigger which adds items to a list as they collide with it / leave it?

I’m throwing out some of my ideas I’ve already thought of but I’m posting this in case someone else has already addressed this problem. i.e. I don’t know if there is already a common solution to this out there? Chances are I’m just totally over thinking this and missing something obvious.

You could wether run an “Vector3.Distance()” against your Object Array, or you simply use Physics.OverlapSphere:

Thanks for the reply =)

So looks like the best way may be to go with the invisible sphere attached to the player concept then if I am understanding this correctly. I will play with this a little later when I get home and see if I can put together a sample project for people. I’m guessing someone else would want this.

Ok here is my contribution if anyone else needs something like this…

I don’t have time to make a project or a package but here is the script that makes everything happen…

To setup a basic version of this put a couple cubes in an empty scene. IMPORTANT: give them a tag named “item” and make sure they have a box collider, mesh collider etc… doesn’t matter what kind of collider they just need one.

Create a small cube in the scene and name it arrow. This is the object that floats over your selection… I put a material on mine that looks like a gold arrow thus the name. Uncheck this cube’s mesh renderer so that it is invisible.

Drop the below script onto your main camera.

private var target : Transform;  	// Object that is selected
private var arrow : Transform;		// Object that floats over selection, default render state is enabled = false
private var currentSelection = 0;	// Current index of selection in array of objects inside the overlap sphere
private var clearSelect = false;	// Needed for when there is nothing in range to select
private var nextSelectOk = true;	// Used as a toggle for when user has released tab key

var overlapDistance = 3;	// The Size of the sphere that should check for items...
var offset = Vector3.up;    // Units in world space to offset; 1 unit above object by default
var avatar : Transform;		// The object that is the center of the selections' transform
var fpsview = true;			// If true then object that contains script is set as the center
							// probably the main camera.. if false then above avatar transform needs to be set as the center
							// Could toggle these two variables with another script to switch between fps and 3rd person views.

function Start () {
	arrow = GameObject.Find("arrow").transform;
}

function Update () {
	if(Input.GetKey("tab")){
		if (nextSelectOk){
			nextSelectOk = false;
			makeSelection();
			if (clearSelect){
				// Do Nothing
				arrow.renderer.enabled = false;
				clearSelect = false;
			}else{
				if (target != null){
			   		arrow.renderer.enabled = true;
				}
			}
		}
	}else if (Input.GetKey("escape")){
		// TODO: Stuff in the comment below
		// We first need to close any windows.. 2nd esc press checks for deselect and or prompts log out, 3rd press prompts log out
		arrow.renderer.enabled = false;
		currentSelection = 0;
		target = null;
	}else{
		nextSelectOk = true;	
	}
	
	// This is executed every update even if tab key is not pressed..
	if (target != null){
		if (checkSelection(target)){
			arrow.transform.LookAt(Camera.main.transform);
			arrow.transform.position = target.position + offset;
		}else{
			arrow.renderer.enabled = false;
			currentSelection = 0;
			target = null;
		}
	}
}

function checkSelection(tmptarget : Transform){
	if (fpsview){
		if(Vector3.Distance(tmptarget.position, transform.position) < overlapDistance){
			return true;
		}else{
			return false;	
		}
	}else{
		if(Vector3.Distance(tmptarget.position, avatar.position) < overlapDistance){
			return true;
		}else{
			return false;	
		}		
	}
}

function makeSelection(){
	var tmpcolliders : Collider[];
	if (fpsview){
		tmpcolliders = new Physics.OverlapSphere(transform.position, overlapDistance);
	}else{
		tmpcolliders = new Physics.OverlapSphere(avatar.position, overlapDistance);
	}
	if (tmpcolliders.length != null){
		var colliders : Collider[];
		var tmpi = 0;
		for (var i = 0; i < tmpcolliders.length; i++){
			if (tmpcolliders[i].tag == "item" || tmpcolliders[i].tag == "Player"){
				if (colliders == null){
					colliders = new Collider[1];
				}else{
					var collidersBack : Collider[] = new Collider[colliders.length];
					for (var c = 0; c < colliders.length; c++){
						collidersBack[c] = colliders[c];
					}
					colliders = new Collider[tmpi+1];
					for (var p = 0; p < collidersBack.length; p++){
						colliders[p] = collidersBack[p];
					}
				}
				
				colliders[tmpi] = tmpcolliders[i];
				tmpi++;
			}
		}
		if (colliders == null){
			clearSelect = true;
			currentSelection = 0;
			return;
		}else if (colliders.length == 1){
			currentSelection = 0;
		}else{
			if (colliders.length > currentSelection+1){
				currentSelection++;
			}else{
				currentSelection = 0;
			}
		}
		if (target == colliders[currentSelection].transform){
			// Do Nothing
			return;
		}else{
			target = colliders[currentSelection].transform;
		}
	}
}

When you press your tab key if there are any item tagged objects with colliders within the overlap sphere size you decided it should make the arrow cube appear above those objects. If you have multiple items within range you can then keep pressing tab to cycle through them.

If in the inspector you drag a selected cube around you will see that the arrow cube follows it and if far enough away will deselect the cube since it is now out of range.

Possible Improvements:

Beyond the fact that the code itself has not been checked for efficiency…

This script checks for item tag and Player tag… You could add more tags and other key combos such as tab+p for closest player or tab+i for closest item etc…

I would like the arrow cube to simply go to the edge of the screen if the selection is behind the camera in fps view so you know which direction to turn until the object is in view then have the arrow go over the object as it should.

Add a key handler for the enter key and a function to handle opening doors, picking up items for your inventory, attacking etc…

Oh I should also note that once you put a character controller in the scene for your player it will select yourself since these have a Player tag. An option to allow self selection to be turned on or off would be a good idea… I have it on because in my case I’d like my players to be able to select themselves.

Hope this helps someone! If you add to it or make some efficiency improvements to the code please post your updates here so we can all benefit from it. Thanks!