How to make a character follow a wall?

I’m trying to make NPC AI for a character that a want to walk around in the quake3level. I can make the character move forward but I don’t know how to insert the intelligence that makes the character turn at a wall and follow a plan around in the level. Can you tell me a strategy for doing so?

[java]package adventure;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Image;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.BlenderKey;
import com.jme3.asset.plugins.HttpZipLocator;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.objects.PhysicsCharacter;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.MaterialList;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.plugins.ogre.OgreMeshKey;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.MouseInput;
public class Q3World extends SimpleApplication implements ActionListener,
AnimEventListener, Playable {
private Node gameLevel;
private PhysicsCharacter player;
private static boolean useHttp = false;
private static World world;
private static Person person;
private static Player dplayer;
private BulletAppState bulletAppState;
private AnimChannel channel;
private AnimControl control;
// character
CharacterControl character;
CharacterControl goblincharacter;
Node model;
// temp vectors
Vector3f walkDirection = new Vector3f();
Spatial goblin;
RigidBodyControl terrainPhysicsNode;

// animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
// camera
boolean left = false, right = false, up = false, down = false, attack=false;
ChaseCamera chaseCam;

FilterPostProcessor fpp;
private Spatial sceneModel;

private RigidBodyControl landscape;

public static void main(String[] args) {

	File file = new File("quake3level.zip");
	if (!file.exists()) {
		useHttp = true;
	}
	Q3World app = new Q3World();
	app.start();
}

@Override
public void simpleInitApp() {
	bulletAppState = new BulletAppState();
	bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
	stateManager.attach(bulletAppState);
	setupKeys();
	DirectionalLight dl = new DirectionalLight();
	dl.setColor(ColorRGBA.White.clone().multLocal(2));
	dl.setDirection(new Vector3f(-1, -1, -1).normalize());
	rootNode.addLight(dl);
	AmbientLight am = new AmbientLight();
	am.setColor(ColorRGBA.White.mult(2));
	rootNode.addLight(am);

	if (useHttp) {
		assetManager
				.registerLocator(
						"http://jmonkeyengine.googlecode.com/files/quake3level.zip",
						HttpZipLocator.class);
	} else {
		assetManager.registerLocator("quake3level.zip", ZipLocator.class);
	}

	// create the geometry and attach it
	MaterialList matList = (MaterialList) assetManager
			.loadAsset("Scene.material");
	OgreMeshKey key = new OgreMeshKey("main.meshxml", matList);
	gameLevel = (Node) assetManager.loadAsset(key);
	gameLevel.setLocalScale(0.1f);
	gameLevel.addControl(new RigidBodyControl(0));
	getPhysicsSpace().addAll(gameLevel);
	rootNode.attachChild(gameLevel);
	getPhysicsSpace().addAll(gameLevel);
	createCharacters();
	setupChaseCamera();
	setupAnimationController();
	setupFilter();
}

private void setupFilter() {
	FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
	BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
	fpp.addFilter(bloom);
	viewPort.addProcessor(fpp);
}

private PhysicsSpace getPhysicsSpace() {
	return bulletAppState.getPhysicsSpace();
}

private void setupKeys() {
	inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
	inputManager.addListener(this, "wireframe");
	inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
	inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
	inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
	inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
	inputManager.addMapping("CharSpace",
			new KeyTrigger(KeyInput.KEY_SPACE));
	inputManager
			.addMapping("CharShoot",  new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
	inputManager.addListener(this, "CharLeft");
	inputManager.addListener(this, "CharRight");
	inputManager.addListener(this, "CharUp");
	inputManager.addListener(this, "CharDown");
	inputManager.addListener(this, "CharSpace");
	inputManager.addListener(this, "CharShoot");
}

private void createCharacters() {
	CapsuleCollisionShape capsule = new CapsuleCollisionShape(0.05f, 0.05f);
	character = new CharacterControl(capsule, 2);
	character.setJumpSpeed(20f);
	model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
	model.scale(0.05f, 0.05f, 0.05f);
	model.addControl(character);
	character.setPhysicsLocation(new Vector3f(55, 15, -60));
	model.setShadowMode(ShadowMode.CastAndReceive);
	character.setViewDirection(new Vector3f(1, 0, 0));
	rootNode.attachChild(model);
	getPhysicsSpace().add(character);
	BlenderKey blenderKey = new BlenderKey("Models/Oto/Oto.mesh.xml");
	Spatial man = (Spatial) assetManager.loadModel(blenderKey);
	man.setLocalTranslation(new Vector3f(67, 15, -60));
	man.setShadowMode(ShadowMode.CastAndReceive);
	rootNode.attachChild(man);
	
	goblin = assetManager.loadModel("objects/goblin.j3o");
	
	goblin.scale(4f, 4f, 4f);
	//goblin.setLocalTranslation(60, 3.5f, -60);
	goblincharacter = new CharacterControl(capsule, 2);
	goblin.addControl(goblincharacter);
	goblincharacter.setPhysicsLocation(new Vector3f(60, 3.5f, -60));
	//goblincharacter.setViewDirection(new Vector3f(1, 0, 0));
	//character.setWalkDirection(new Vector3f(1, 0, 0));
	
	
	
	control = goblin.getControl(AnimControl.class);
	control.addListener(this);
	channel = control.createChannel();

	for (String anim : control.getAnimationNames())
		System.out.println("goblin can:"+anim);
	channel.setAnim("walk");
	
	rootNode.attachChild(goblin);
	getPhysicsSpace().add(goblincharacter);
	Spatial monster = assetManager.loadModel("objects/creatures/monster/monster.packed.j3o");
	monster.scale(2f, 2f, 2f);
	monster.setLocalTranslation(new Vector3f(53, 3, -55));
	rootNode.attachChild(monster);

}

private void setupChaseCamera() {
	flyCam.setEnabled(false);
	chaseCam = new ChaseCamera(cam, model, inputManager);
	chaseCam.setDefaultDistance(27);
	
	
	
}

private void setupAnimationController() {
	animationControl = model.getControl(AnimControl.class);
	animationControl.addListener(this);
	animationChannel = animationControl.createChannel();
    //shootingChannel = animationControl.createChannel();

}

@Override
public void simpleUpdate(float tpf) {
	 
	goblincharacter.setWalkDirection(goblin.getLocalRotation().mult(Vector3f.UNIT_Z).multLocal(0.4f));
	Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);
	Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
	camDir.y = 0;
	camLeft.y = 0;
	walkDirection.set(0, 0, 0);
	if (left) {
		walkDirection.addLocal(camLeft);
	}
	if (right) {
		walkDirection.addLocal(camLeft.negate());
	}
	if (up) {
		walkDirection.addLocal(camDir);
	}
	if (down) {
		walkDirection.addLocal(camDir.negate());
	}
	//if (attack) {
		//animationChannel.setAnim("Attack1");
		//animationChannel.setLoopMode(LoopMode.DontLoop);
	//}
	if (!character.onGround()) {
		airTime = airTime + tpf;
	} else {
		airTime = 0;
	}
	if (walkDirection.length() == 0) {
		if (!"Idle1".equals(animationChannel.getAnimationName())) {
			animationChannel.setAnim("Idle1", 1f);
		}
	} else {			
		character.setViewDirection(walkDirection.negate());
		if (airTime > .3f) {
			if (!"stand".equals(animationChannel.getAnimationName())) {
				animationChannel.setAnim("Idle1");
			}
		} else if (!"Walk".equals(animationChannel.getAnimationName())) {
			animationChannel.setAnim("Walk", 1f);
		}
	}
	character.setWalkDirection(walkDirection);
}

/*Ninja can: Walk

Ninja can: Kick
Ninja can: JumpNoHeight
Ninja can: Jump
Ninja can: Spin
Ninja can: Attack1
Ninja can: Idle1
Ninja can: Attack3
Ninja can: Idle2
Ninja can: Attack2
Ninja can: Idle3
Ninja can: Stealth
Ninja can: Death2
Ninja can: Death1
Ninja can: HighJump
Ninja can: SideKick
Ninja can: Backflip
Ninja can: Block
Ninja can: Climb
Ninja can: Crouch*/

public void onAction(String binding, boolean value, float tpf) {
	
	
	
	if (binding.equals("CharLeft")) {
		if (value) {
			left = true;
		} else {
			left = false;
		}
	} else if (binding.equals("CharRight")) {
		if (value) {
			right = true;
		} else {
			right = false;
		}
	} else if (binding.equals("CharUp")) {
		if (value) {
			up = true;
		} else {
			up = false;
		}
	} else if (binding.equals("CharDown")) {
		if (value) {
			down = true;
		} else {
			down = false;
		}
	} else if (binding.equals("CharSpace")) {
		character.jump();
	} else if (binding.equals("CharShoot") && value) {
        //bulletControl();
        Vector3f origin    = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.0f);
        Vector3f direction = cam.getWorldCoordinates(inputManager.getCursorPosition(), 0.3f);
        //direction.subtractLocal(origin).normalizeLocal();
		//character.setWalkDirection(location);
        System.out.println("origin"+origin);
        System.out.println("direction"+direction);
		character.setViewDirection(direction);
		animationChannel.setAnim("Attack3");
		animationChannel.setLoopMode(LoopMode.DontLoop);
    }
}

public void onAnimCycleDone(AnimControl control, AnimChannel channel,
		String animName) {
	if (channel == shootingChannel) {
		channel.setAnim("Idle1");
	}
}

public void onAnimChange(AnimControl control, AnimChannel channel,
		String animName) {
}

// Load an image from the net, making sure it has already been
// loaded when the method returns
public Image loadPicture(String imageName) {
	return null;
}

// Load and play a sound from /usr/local/hacks/sounds/
public void playSound(String name) {
	URL u = null;
	try {
		u = new URL("file:" + "/usr/local/hacks/sounds/" + name + ".au");
	} catch (MalformedURLException e) {
	}
	AudioClip a = Applet.newAudioClip(u);
	a.play();
}

}[/java]

Navigation mesh, pathfinding, some sort of planning system…