2011-01-17 2 views
13

오랫동안 자바 프로그래머가 천천히 스칼라를 배우고 있으며, 내 마음이 기능적으로 글쓰기의 개념을 둘러싼다고 생각한다. 지금은 움직이는 2D 텍스처에 간단한 비주얼 라이저를 작성하려고합니다. (물건 무고한을 보호하기 위해 변경되었습니다) 명령형 접근 방식은 간단하다, 나는 당신의 대부분은이 코드 상대적으로 유비쿼터스 블록을 인식 확신 :스칼라 게임 프로그래밍 : 기능적 스타일로 객체 위치를 전진

class MovingTexture(var position, var velocity) extends Renders with Advances { 
    def render : Unit = {...} 
    def advance(milliseconds : Float) : Unit = { 
     position = position + velocity * milliseconds 
    } 
} 

이 코드는, 그러나 그것은 가지고, 잘 작동합니다 톤수의 가변 상태와 그 기능은 부작용으로 가득차 있습니다. 나는 이걸 가지고 도망 갈 수 없다. 거기에 이 더 좋은 방법이되어야한다!

누구나이 단순한 문제에 대해 훌륭하고 우아하고 기능적인 해결책을 갖고 있습니까? 누구든지 이러한 종류의 문제를 해결하는 방법에 대해 더 많은 것을 배울 수있는 출처에 대해 알고 있습니까?

답변

11

게임은 종종 고성능 업무이기도하며,이 경우 게임이 변경 될 수있는 상태가 필요한 것임을 알 수 있습니다.

, 대신 수업으로 업데이트 필요하다
case class MovingTexture(position: VecXY, velocity: VecXY) extends Renders with Advances { 
    def advance(ms: Float) = copy(position = position + ms*velocity 
    def accelerate(acc: Float, ms: Float) = copy(velocity = velocity + ms*acc) 
    ... 
} 

이 그들 자신의 새 복사본을 반환 있습니다

그러나,이 경우 간단한 몇 가지 해결책이있다. (테트리스의 경우 큰 비중을 차지할 수 있습니다. 크라이시스의 경우는 그렇게 똑똑하지 않을 수 있습니다.) 문제가 한 단계 뒤로 밀려나는 것처럼 보입니다. MovingTexture의 경우 var가 필요합니다. 맞습니까? 전혀 아님 :

Iterator.iterate(MovingTexture(home, defaultSpeed))(_.advance(defaultStep)) 

이렇게하면 같은 방향으로 끝없는 위치 업데이트 스트림이 생성됩니다. 사용자 입력이나 기타 등등으로 혼합하기 위해 더 복잡한 작업을 수행 할 수 있습니다.

또는,

입니다
class Origin extends Renders { 
    // All sorts of expensive stuff goes here 
} 
class Transposed(val ori: Origin, val position: VecXY) extends Renders with Advances { 
    // Wrap TextureAtOrigin with inexpensive methods to make it act like it's moved 
    def moving(vel: VecXY, ms: Float) = { 
    Iterator.iterate(this).(tt => new Transposed(tt.ori, position+ms*vel)) 
    } 
} 

는 헤비급 일들이 업데이트되지 그들은 당신이 그들을 변경 원하는 방식으로 변경 한 것처럼 그들 보이게 그들의 가벼운 무게 전망이 결코 가질 수 있습니다 .

+1

(모든 답변 중에서) 일반적인 컨센서스는 현재 상태를 나타내는 내 개체의 복사본을 만드는 것일 것 같습니다. 이것은 정확히 내가 원하는 것이 아니지만 _this_ 대답은 값의 변화를 표현하는 반복자를 정의하는 것과 같은 생각을위한 흥미로운 음식을 제시합니다. (값의 상태를 표현하는 것보다 ...) 변수가 어떻게 변화하는지 (미적분과 같은 종류)를 나타내는 일종의 함수 (또는 반복)로서의 위치를 ​​표현할 수있는 방법을 찾으십시오. 그래도 멋진 것들을 저에게 소개해 주셔서 감사합니다! – jtb

14

하나의 stackoverflow 응답 공간에 적합 할 수있는 것보다이 대답에 더 많은 방법이 있지만 기능적 반응 프로그래밍이라는 것을 사용하는 것이 가장 좋은 질문입니다. 기본 아이디어는 시간 가변 또는 상호 작용하는 양을 가변 변수가 아닌 각각의 시간 퀀텀에 대한 값의 불변 스트림으로 표현하는 것입니다. 트릭은 각 값이 잠재적으로 무한한 값의 스트림으로 표현되는 동안 스트림이 느리게 계산되므로 (메모리가 필요성까지 채워지지 않도록), 스트림 값은 시간의 퀀텀을 찾지 않는다는 것입니다. 과거 (이전 계산이 가비지 수집 될 수 있도록). 계산은 훌륭하게 작동하고 변경할 수 없지만 "보고있는"계산 부분은 시간이 지나면 바뀝니다.

이것은 모두 복잡하며 이와 같은 스트림을 결합하는 것은 특히 메모리 누수를 피하고 스레드 안전하고 효율적인 방식으로 모든 작업을 수행하려는 경우 까다로운 작업입니다. Functional Reactive Programming을 구현 한 스칼라 라이브러리가 몇 가지 있지만 성숙도는 아직 높지 않습니다. 가장 흥미로운 것은 아마도 scala.react이고, here으로 묘사됩니다.

+0

이 문제에 대해 생각하는 한 머리에 못을 박은 것 같습니다. 당신은 당신이 당신의 물건을 알고있는 것처럼 들린다. 나는 간단한 코드 예제를 제공했으면 좋겠다.하지만 아마도 당신이 맞을 것이다.이 문제는 너무 광범위해서 표현할 수있는 간결한 해결책이 아니다. – jtb

+0

글쎄, 나는 실제로 대화 형 측면에서 많은 작업을하지 않으며 분노로 FRP를 사용한 적이 없다. 독서를 완료하고, 이론을 알고 있지만 실제로 코드를 자르지 마십시오. –

+0

글쎄, 글쎄, 나는 확실히 FRP에 대해 들어 본 적이 없기 때문에 언급했다. 그래서 적어도 나는 더 많은 정보를 얻었고 나는 흥미로운 논문을 읽어야한다. 감사! – jtb

1

다음은 상태를 직접 변경하는 대신 복사본을 반환하는 방식을 사용하는 일부 코드 샘플입니다.적어도 이런 종류의 접근법에 대한 좋은 점은 서버 측에서 트랜잭션 유형 의미를 쉽게 구현할 수 있다는 것입니다. 업데이트를 수행하는 중에 문제가 발생하면 일관된 상태로 업데이트 된 모든 것을 계속 유지하는 것이 쉽지 않습니다.

아래 코드는 내가하고있는 게임 서버에서 가져온 것으로, 여러분이하고있는 것과 비슷한 것을합니다. 그것은 타임 슬라이스에서 움직이는 객체들을 추적하기위한 것입니다. 이 접근 방식은 Dave Griffith가 제안한 것만 큼 놀라운 것은 아니지만 숙고를 위해 어느 정도 유용 할 수 있습니다. 스칼라의 경우 클래스에 대한

case class PosController(
    pos: Vector3 = Vector3.zero, 
    maxSpeed: Int = 90, 
    velocity: Vector3 = Vector3.zero, 
    target: Vector3 = Vector3.zero 
) { 
    def moving = !velocity.isZero 

    def update(elapsed: Double) = { 
     if (!moving) 
      this 
     else { 
      val proposedMove = velocity * elapsed 
      // If we're about to overshoot, then stop at the exact position. 
      if (proposedMove.mag2 > pos.dist2(target)) 
       copy(velocity = Vector3.zero, pos = target) 
      else 
       copy(pos = pos + proposedMove) 
     } 
    } 

    def setTarget(p: Vector3) = { 
     if (p == pos) 
      this 
     else { 
      // For now, go immediately to max velocity in the correct direction. 
      val direction = (p - pos).norm 
      val newVel = direction * maxSpeed 
      copy(velocity = direction * maxSpeed, target = p) 
     } 
    } 

    def setTargetRange(p: Vector3, range: Double) = { 
     val delta = p - pos 
     // Already in range? 
     if (delta.mag2 < range * range) 
      this 
     else { 
      // We're not in range. Select a spot on a line between them and us, at max range. 
      val d = delta.norm * range 
      setTarget(p - d) 
     } 
    } 

    def eta = if (!moving) 0.0 else pos.dist(target)/maxSpeed 
} 

하나 좋은 점은 그들이 방금있는 매개 변수가 변경 통과 너와의 복사() 메소드를 생성하고 다른 사람이 동일한 값을 유지한다는 것입니다. 대/소문자 클래스를 사용하지 않는다면이 코드를 직접 코딩 할 수 있지만 클래스에있는 값을 변경할 때마다 복사 메서드를 업데이트해야합니다.

리소스에 관해서는 실제로 얼터너티브를 사용하는 것 외에 얼랭 (Erlang)에서 일하는 데 시간을 보내고있었습니다. 나는 두 편의 얼랭 (Erlang) 저서를 가지고 모든 예제를 신중하게 연구했습니다. Erlang에서 몇 가지 작업을 수행하도록 강요하는 것은 불변의 데이터로 작업하는 것을 훨씬 편하게 해주었습니다.

+0

이상하게도 Erlang은 필자의 연약한 두뇌가 스칼라와 함께 할 일이 많이 남아 있지만 필자가 픽업 (또는 하스켈)을 시도 할 다음 언어이다. 나는 네가 좋아하는 것을 좋아한다! 나는 여전히 모든 반복에서 객체의 복사본을 만드는 것보다 변수가 어떻게 변하는지를 나타내는 더 일반적인 방법이 있어야한다고 생각한다. 당신은 var보다는 시간이 지남에 따라 위치의 함수로 위치를 표현하는 것을 실험 해본 적이 있습니까? – jtb

+0

나는 그렇지 않다. 나는 여러 개의 val로 구성된 하나의 var 내에 액터에 대한 모든 상태를 유지하기 시작했습니다. 예를 들어 위치 컨트롤러는 하나의 val이고 두뇌 상태는 다른 val입니다. 이들 모두는 하나의 상태 변수로 변환됩니다.하나를 변경하고 다른 하나를 변경하는 메소드가 있고 중간에 예외가 발생하는 경우 3 개 연산 모두 실패하거나 3 개 모두 실패하고 상태가 유지된다는 보장이 있습니다. 내가 시간의 함수로 표현하지 않은 이유는 목표 위치가 때로는 두뇌 클래스에 의해, 때로는 플레이어에 의해 제어되기 때문입니다. – Unoti

+0

당신은 스칼라에서 당신의 두뇌를 감싸는 것에 대해 많이 언급했었다. 필자는 Erlang보다 Scala가 더 어렵다는 것을 발견했다. 다른 사람들은 다르게 느끼지만, Erlang vs. Scala에 대한 학습 과정에서 http://tango11.com/news/scala-complexity-vs-erlang-complexity/에 관심을 가질만한 의견을 적었습니다. – Unoti

2

인터랙티브 응용 프로그램을 프로그래밍하는 데 전적으로 기능적인 접근법에 대해 약간의 길이가되는 "세계를 디자인하는 방법"이라는 브로셔가 있습니다 ("프로그램 설계 방법"작성자가 작성).

기본적으로 "세계"(모든 게임 상태를 포함하는 데이터 유형) 및 "tick"(world -> world 유형) 및 "onkeypress"(key * world 유형의) -> 세계). "render"함수는 월드를 취하여 장면을 반환 한 다음 장면을 "실제"렌더러로 전달합니다.

0

이 짧은 일련의 기사는 프로그래밍 문제를 기능적으로 해결하는 데있어 초보자로서 나를 도왔습니다. 게임은 Retro (Pac Man)이지만 프로그래머는 그렇지 않습니다. http://prog21.dadgum.com/23.html