2D 플랫 포머에서 고르지 않은 벽에 캐릭터를 걸 으려면 어떻게해야합니까?


11

나는 옆으로, 거꾸로 등 어떤 각도로든 유기 표면을 걸을 수있는 플레이 가능한 캐릭터를 원합니다. 90도 각도의 직선 대신 경사 및 곡선 형상의 "유기"레벨.

나는 현재 AS3 (보통 아마추어 경험)에서 일하고 있으며 기본 중력 기반 물리학에 Nape (거의 초보자)를 사용하고 있습니다.이 걷기 기술은 명백한 예외입니다.

Nape 구속 조건을 사용하여 이런 종류의 보행을 수행하는 절차적인 방법이 있습니까? 아니면 수평면의 윤곽을 따라 걷는 "경로"를 명시 적으로 만들어 걷는 움직임을 제한하는 것이 가장 좋을까요?


명확히하기 위해 : 당신은 당신의 캐릭터가 당신의 레벨의 벽과 천장에 '고착'할 수 있도록 하시겠습니까?
Qqwy

맞습니다.
Eric N

답변:


9

여기에 완전한 학습 경험이 있으며 Nape의 내부 방법을 사용하여 원하는 운동의 거의 기능적인 버전을 얻었습니다. 이 코드는 모두 Spider 클래스 내에 있으며 부모 클래스 인 Level 클래스에서 일부 속성을 가져옵니다.

다른 클래스와 메소드는 대부분 Nape 패키지의 일부입니다. 내 수입 목록의 관련 부분은 다음과 같습니다.

import flash.events.TimerEvent;
import flash.utils.Timer;

import nape.callbacks.CbEvent;
import nape.callbacks.CbType;
import nape.callbacks.InteractionCallback;
import nape.callbacks.InteractionListener;
import nape.callbacks.InteractionType;
import nape.callbacks.OptionType;
import nape.dynamics.Arbiter;
import nape.dynamics.ArbiterList;
import nape.geom.Geom;
import nape.geom.Vec2;

먼저, 거미가 무대에 추가되면 충돌에 대한 리스너를 Nape 월드에 추가합니다. 개발을 계속할수록 충돌 그룹을 차별화해야합니다. 현재이 콜백은 모든 바디가 다른 바디와 충돌 할 때 기술적으로 실행됩니다.

        var opType:OptionType = new OptionType([CbType.ANY_BODY]);
        mass = body.mass;
        // Listen for collision with level, before, during, and after.
        var landDetect:InteractionListener =  new InteractionListener(CbEvent.BEGIN, InteractionType.COLLISION, opType, opType, spiderLand)
        var moveDetect:InteractionListener =  new InteractionListener(CbEvent.ONGOING, InteractionType.COLLISION, opType, opType, spiderMove);
        var toDetect:InteractionListener =  new InteractionListener(CbEvent.END, InteractionType.COLLISION, opType, opType, takeOff);

        Level(this.parent).world.listeners.add(landDetect);
        Level(this.parent).world.listeners.add(moveDetect);
        Level(this.parent).world.listeners.add(toDetect);

        /*
            A reference to the spider's parent level's master timer, which also drives the nape world,
            runs a callback within the spider class every frame.
        */
        Level(this.parent).nTimer.addEventListener(TimerEvent.TIMER, tick);

콜백은 거미의 "상태"속성 (부울 집합)을 변경하고 나중에 걷는 논리에 사용하기 위해 Nape 충돌 중재자를 기록합니다. 또한 타이머를 설정하고 지워서 거미가 최대 100ms 동안 수평면과의 접촉을 잃어 버려 세계 중력이 다시 잡히도록합니다.

    protected function spiderLand(callBack:InteractionCallback):void {
        tArbiters = callBack.arbiters.copy();
        state.isGrounded = true;
        state.isMidair = false;
        body.gravMass = 0;
        toTimer.stop();
        toTimer.reset();
    }

    protected function spiderMove(callBack:InteractionCallback):void {
        tArbiters = callBack.arbiters.copy();
    }

    protected function takeOff(callBack:InteractionCallback):void {
        tArbiters.clear();
        toTimer.reset();
        toTimer.start();
    }

    protected function takeOffTimer(e:TimerEvent):void {
        state.isGrounded = false;
        state.isMidair = true;
        body.gravMass = mass;
        state.isMoving = false;
    }

마지막으로, 상태와 레벨 지오메트리와의 관계에 따라 스파이더에 적용 할 힘을 계산합니다. 나는 대부분 의견이 스스로 말하도록 할 것이다.

    protected function tick(e:TimerEvent):void {
        if(state.isGrounded) {
            switch(tArbiters.length) {
                /*
                    If there are no arbiters (i.e. spider is in midair and toTimer hasn't expired),
                    aim the adhesion force at the nearest point on the level geometry.
                */
                case 0:
                    closestA = Vec2.get();
                    closestB = Vec2.get();
                    Geom.distanceBody(body, lvBody, closestA, closestB);
                    stickForce = closestA.sub(body.position, true);
                    break;
                // For one contact point, aim the adhesion force at that point.
                case 1:
                    stickForce = tArbiters.at(0).collisionArbiter.contacts.at(0).position.sub(body.position, true);
                    break;
                // For multiple contact points, add the vectors to find the average angle.
                default:
                    var taSum:Vec2 = tArbiters.at(0).collisionArbiter.contacts.at(0).position.sub(body.position, true);
                    tArbiters.copy().foreach(function(a:Arbiter):void {
                        if(taSum != a.collisionArbiter.contacts.at(0).position.sub(body.position, true))
                            taSum.addeq(a.collisionArbiter.contacts.at(0).position.sub(body.position, true));
                    });

                    stickForce=taSum.copy();
            }
            // Normalize stickForce's strength.
            stickForce.length = 1000;
            var curForce:Vec2 = new Vec2(stickForce.x, stickForce.y);

            // For graphical purposes, align the body (simulation-based rotation is disabled) with the adhesion force.
            body.rotation = stickForce.angle - Math.PI/2;

            body.applyImpulse(curForce);

            if(state.isMoving) {
                // Gives "movement force" a dummy value since (0,0) causes problems.
                mForce = new Vec2(10,10);
                mForce.length = 1000;

                // Dir is movement direction, a boolean. If true, the spider is moving left with respect to the surface; otherwise right.
                // Using the corrected "down" angle, move perpendicular to that angle
                if(dir) {
                    mForce.angle = correctAngle()+Math.PI/2;
                } else {
                    mForce.angle = correctAngle()-Math.PI/2;
                }
                // Flip the spider's graphic depending on direction.
                texture.scaleX = dir?-1:1;
                // Now apply the movement impulse and decrease speed if it goes over the max.
                body.applyImpulse(mForce);
                if(body.velocity.length > 1000) body.velocity.length = 1000;

            }
        }
    }

내가 찾은 진짜 끈적 거리는 부분은 거미가 날카로운 각도에 도달하거나 깊은 계곡에 앉아있는 다중 접점 시나리오에서 실제로 원하는 이동 방향에 있어야한다는 것입니다. 특히, 접착력에 대한 합산 된 벡터를 고려할 때, 그 힘은 수직이 아닌 이동하려는 방향에서 멀어 지므로 그에 대응해야합니다. 그래서 움직임 벡터의 각도의 기초로 사용할 접점 중 하나를 선택하는 논리가 필요했습니다.

접착력의 "풀"의 부작용은 거미가 날카로운 오목한 각도 / 곡선에 도달 할 때 약간의 망설임이지만 실제로는 모양과 느낌의 관점에서 현실적입니다. 그대로 두십시오. 필요한 경우이 방법의 변형을 사용하여 접착력을 계산할 수 있습니다.

    protected function correctAngle():Number {
        var angle:Number;
        if(tArbiters.length < 2) {
            // If there is only one (or zero) contact point(s), the "corrected" angle doesn't change from stickForce's angle.
            angle = stickForce.angle;
        } else {
            /*
                For more than one contact point, we want to run perpendicular to the "new" down, so we copy all the
                contact point angles into an array...
            */
            var angArr:Array = [];
            tArbiters.copy().foreach(function(a:Arbiter):void {
                var curAng:Number = a.collisionArbiter.contacts.at(0).position.sub(body.position, true).angle;
                if (curAng < 0) curAng += Math.PI*2;
                angArr.push(curAng);
            });
            /*
                ...then we iterate through all those contact points' angles with respect to the spider's COM to figure out
                which one is more clockwise or more counterclockwise, depending, with some restrictions...
                ...Whatever, the correct one.
            */
            angle = angArr[0];
            for(var i:int = 1; i<angArr.length; i++) {
                if(dir) {
                    if(Math.abs(angArr[i]-angle) < Math.PI)
                        angle = Math.max(angle, angArr[i]);
                    else
                        angle = Math.min(angle, angArr[i]);
                }
                else {
                    if(Math.abs(angArr[i]-angle) < Math.PI)
                        angle = Math.min(angle, angArr[i]);
                    else
                        angle = Math.max(angle, angArr[i]);
                }
            }
        }

        return angle;
    }

이 논리는 지금까지 내가 원하는 것을하고있는 것처럼 거의 "완벽하다". 그러나 미묘한 외관상의 문제가 있지만, 거미의 그래픽을 접착력이나 이동 력에 맞추려고하면 거미가 움직임의 방향으로 "기울"게된다는 것을 알게됩니다. 다리가 두 개인 육상 선수이지만 그는 그렇지 않으며 각도는 지형의 변화에 ​​매우 민감하므로 거미가 약간의 충돌을 가하면 거미가 흔들립니다. 나는 거미의 방향을 더 부드럽고 현실적으로 만들기 위해 Byte56의 솔루션을 변형하여 주변 풍경을 샘플링하고 그 각도를 평균화 할 수 있습니다.


1
앞으로 방문자를 위해 세부 정보를 게시 해 주셔서 감사합니다.
MichaelHouse

8

캐릭터가 접촉하는 "스틱"표면을 표면의 반대 법선을 따라 힘을 가하는 것은 어떻습니까? 힘은 표면에 닿아있는 한 유지되며 활성화되어있는 동안 중력을 무시합니다. 따라서 천장에서 뛰어 내리면 바닥으로 떨어질 것으로 예상됩니다.

이 기능을 원활하게 구현하고 쉽게 구현할 수 있도록 다른 기능을 구현하고 싶을 것입니다. 예를 들어, 캐릭터가 터치하는 것 대신 캐릭터 주위에 원을 사용하고 반전 된 법선을 요약합니다. 이 엉뚱한 페인트 이미지가 보여주는 것처럼 :

여기에 이미지 설명을 입력하십시오

(표현 된 거미 모양은 Byte56의 속성입니다)

파란색 선은 해당 지점의 표면에 대한 법선입니다. 녹색 선은 거미에 가해지는 힘입니다. 빨간색 원은 거미가 사용할 법선을 찾는 범위를 나타냅니다.

이것은 거미가 "손잡이를 잃지"않고 지형에 약간의 울퉁불퉁 함을 허용합니다. 그 문제에 대한 원의 크기와 모양에 대한 실험은 거미를 아래로 향한 반원, 다리를 둘러싼 직사각형을 사용하십시오.

이 솔루션을 사용하면 캐릭터가 따라갈 수있는 특정 경로를 처리 할 필요없이 물리를 켠 상태로 유지할 수 있습니다. 또한 이해하기 쉽고 이해하기 쉬운 정보를 사용하고 있습니다 (정상). 마지막으로 역동적입니다. 드로잉하는 지오메트리에 대한 법선을 쉽게 얻을 수 있기 때문에 세상의 모양을 바꾸는 것조차 쉽게 설명 할 수 있습니다.

거미의 범위 내에 얼굴이 없으면 일반 중력이 적용됩니다.


합산 법선은 현재 솔루션에서 날카로운 오목한 모서리로 발생하는 문제를 해결할 수도 있지만 AS3에서 어떻게 얻는 지 잘 모르겠습니다.
Eric N

미안하지만 나도 잘 모른다. 지형을 생성 할 때 자신을 유지하기 위해 필요한 것입니다.
MichaelHouse

2
Nape의 충돌 접점을 감지하고 둘 이상이 있으면 평균을 내릴 수 있기 때문에이 아이디어를 구현했습니다. 평평하거나 볼록한 표면 위로 이동하는 데 필요한 것 같지는 않지만 가장 큰 문제는 거미가 날카로운 모서리를 만났을 때해야 할 일을 해결했습니다. 나의 새로운 답변에서 언급했듯이, 나는 거미의 그래픽을 맞추기 위해이 아이디어를 변형 할 수 있습니다.
Eric N
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.