Game AI & Unity/Java Steering game

[Game AI][Steering Behavior] Task_Wall Avoidance (controllers.AvoidController.java)

bay07 2024. 3. 21. 12:33

 

AvoidController

자동차가 장애물을 피하도록 조작하는 컨트롤러이다. 

시뮬레이션에서 자동차가 장애물을 회피하도록하는 기본적인 제어 로직

package controllers;

import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;

import engine.Car;
import engine.Game;
import engine.GameObject;
import engine.RotatedRectangle;
import engine.Vector;

 

먼저 필요한 패키지와 클래스를 import 해준다

 

package controllers;

import java.awt.KeyEventDispatcher;

public class AvoidController extends Controller {

	// 키보드 설정 

	
	private GameObject target;
	
	public AvoidController(GameObject _target) {
	}
	
	public Vector seek(Car subject) {
	}
	
	public Vector seekTurn(Car subject, double x, double y) {
	}
	
	public boolean collideBox(Car subject, Game game, double angle, double distance) {
	}
	
	public void update(Car subject, Game game, double delta_t, double controlVariables[]) {
	}
}

 

seek(Car subject) 주어진 자동차 객체 subject를 기준으로 목표 지점(target 객체의 위치)로 향하는 방향을 계산한다. 목표 지점은 target 객체의 위치입니다. 이를 위해 먼저 자동차의 현재 위치와 목표 지점의 위치 사이의 벡터를 계산하고, 이를 정규화하여 단위 벡터로 만든다. 그런 다음 최대 가속도(MAX_ACCELERATION)를 곱하여 최종 이동 벡터를 계산
seekTurn(Car subject, double x, double y) 주어진 위치(x, y)로 향하는 방향을 계산. 이 메서드는 seek와 비슷하지만 목표 지점이 주어진 위치로 변경된다는 차이가 있다.
collideBox(Car subject, Game game, double angle, double distance) 자동차가 주어진 방향으로 이동했을 때, 충돌이 발생하는지 확인

자동차가 특정 각도와 거리로 이동했을 때 충돌이 발생하는지를 확인. 자동차의 새로운 위치가 주어지면, 해당 위치에 회전된 사각형 충돌 상자를 생성하고, 이 충돌 상자가 게임의 장애물과 충돌하는지를 확인한다.
update(Car subject, Game game, double delta_t, double controlVariables[]) 자동차의 현재 상태를 기반으로 자동차를 제어한다 
주변의 장애물을 감지하고 회피하기 위해 사용된다

자동차의 전방, 좌측, 우측을 캐스팅하여 각 방향으로 이동할 수 있는지 확인하고, 적절한 방향으로 움직이도록 제어한다. 만약 전방에 장애물이 있다면 후진하고, 좌측 또는 우측에 장애물이 있다면 해당 방향으로 회전하여 회피합니다. 그렇지 않으면 목표 지점으로 직진한다.

 

 

 

- 전체 코드 

더보기
package controllers;

import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;

import engine.Car;
import engine.Game;
import engine.GameObject;
import engine.RotatedRectangle;
import engine.Vector;

public class AvoidController extends Controller {
	public static final int key_accelerate = KeyEvent.VK_UP;
	public static final int key_brake = KeyEvent.VK_DOWN;
	public static final int key_left = KeyEvent.VK_LEFT;
	public static final int key_right = KeyEvent.VK_RIGHT;
	    
	// store which keys are currently pressed:
	boolean keyboardState[] = new boolean[KeyEvent.KEY_LAST];
	
	private GameObject target;
	
	public AvoidController(GameObject _target) {
		target = _target;    
	}
	
	public Vector seek(Car subject) {
		Vector c = new Vector(subject.getX(), subject.getY());
		
		Vector car = new Vector(subject.getX(), subject.getY());
		Vector targetCar = new Vector(target.getX(), target.getY());
		c = targetCar.minus(car);
		c = c.normalize();
		c = c.times(MAX_ACCELERATION);
		
		//System.out.println(c.toString());
		return c;
	}
	
	public Vector seekTurn(Car subject, double x, double y) {
		Vector c = new Vector(subject.getX(), subject.getY());
		
		Vector car = new Vector(subject.getX(), subject.getY());
		Vector targetCar = new Vector(x, y);
		c = targetCar.minus(car);
		c = c.normalize();
		c = c.times(MAX_ACCELERATION);
		
		//System.out.println(c.toString());
		return c;
	}
	
	public boolean collideBox(Car subject, Game game, double angle, double distance) {
		double newX = subject.getX() + Math.cos(angle) * distance;
		double newY = subject.getY() + Math.sin(angle) * distance;
		
		RotatedRectangle r = new RotatedRectangle(newX, newY, subject.getImg().getWidth() * 1.0, subject.getImg().getHeight() * 1.0, angle);
	
		for(GameObject obj:game.getObjects()) {
            if(obj.isObstacle() && RotatedRectangle.RotRectsCollision(r, obj.getCollisionBox())) {
            	return true;
            }
        }
		
		return false;
	}
	
	public void update(Car subject, Game game, double delta_t, double controlVariables[]) {
	    controlVariables[VARIABLE_STEERING] = 0;
	    controlVariables[VARIABLE_THROTTLE] = 0;
	    controlVariables[VARIABLE_BRAKE] = 0;
	    double castLength = 25;
	    double moveLength = castLength;
	    
	    boolean left = false;
	    boolean front = false;
	    boolean right = false;
	    
	    for(int i = 1; i < castLength; i ++) {
		    left = collideBox(subject, game, subject.getAngle() - Math.PI/4, i);
		    if(left) {
		    	moveLength = i;
		    	break;
		    }
	    }
	    
	    for(int i = 1; i < castLength; i ++) {
		    front = collideBox(subject, game, subject.getAngle(), i);
		    if(front) {
		    	moveLength = i;
		    	break;
		    }
	    }
	    
	    for(int i = 1; i < castLength; i ++) {
		    right = collideBox(subject, game, subject.getAngle() + Math.PI/4, i);
		    if(right) {
		    	moveLength = i;
		    	break;
		    }
	    }
	    
	    if(left) {
	    	System.out.println("Going Right");
	    	double newX = subject.getX() + Math.cos(subject.getAngle() + Math.PI/4) * moveLength;
			double newY = subject.getY() + Math.sin(subject.getAngle() + Math.PI/4) * moveLength;
			motorControl(subject, seekTurn(subject, newX, newY), controlVariables);
	    } else if(front) {
	    	System.out.println("Going Back");
	    	double newX = subject.getX() + Math.cos(subject.getAngle()) * -moveLength;
			double newY = subject.getY() + Math.sin(subject.getAngle()) * -moveLength;
			motorControl(subject, seekTurn(subject, newX, newY), controlVariables);
	    } else if(right) {
	    	System.out.println("Going Left");
	    	double newX = subject.getX() + Math.cos(subject.getAngle() - Math.PI/4) * moveLength;
			double newY = subject.getY() + Math.sin(subject.getAngle() - Math.PI/4) * moveLength;
			motorControl(subject, seekTurn(subject, newX, newY), controlVariables);
	    } else {
	    	motorControl(subject, seek(subject), controlVariables);
	    }
	}
}