Làm thế nào tôi có thể làm cho một nhân vật đi trên những bức tường không bằng phẳng trong một nền tảng 2D?


11

Tôi muốn có một nhân vật có thể chơi được, có thể "đi lại" trên một bề mặt hữu cơ ở mọi góc độ, kể cả nằm nghiêng và lộn ngược. Bằng các mức "hữu cơ" với các đặc điểm nghiêng và cong thay vì các đường thẳng ở góc 90 độ.

Tôi hiện đang làm việc trong AS3 (kinh nghiệm nghiệp dư vừa phải) và sử dụng Nape (khá nhiều người mới) cho vật lý dựa trên trọng lực cơ bản, mà cơ chế đi bộ này sẽ là một ngoại lệ rõ ràng.

Có một cách thủ tục để làm loại cơ khí đi bộ này, có lẽ sử dụng các ràng buộc Nape? Hoặc tốt nhất là tạo ra các "đường đi" rõ ràng theo các đường viền của các bề mặt cấp và sử dụng chúng để hạn chế chuyển động đi bộ?


Để làm rõ: Bạn muốn làm cho nhân vật của bạn có thể 'dán' trên tường và trần nhà ở cấp độ của bạn?
Qqwy

Đúng rồi.
Eric N

Câu trả lời:


9

Đây là kinh nghiệm học tập hoàn chỉnh của tôi, dẫn đến một phiên bản chức năng khá nhiều của phong trào tôi muốn, tất cả đều sử dụng các phương pháp nội bộ của Nape. Tất cả các mã này đều nằm trong lớp Spider của tôi, lấy một số thuộc tính từ lớp cha của nó, một lớp Level.

Hầu hết các lớp và phương thức khác là một phần của gói Nape. Đây là phần thích hợp trong danh sách nhập khẩu của tôi:

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;

Đầu tiên, khi con nhện được thêm vào sân khấu, tôi thêm người nghe vào thế giới Nape để va chạm. Khi tôi phát triển hơn nữa, tôi sẽ cần phân biệt các nhóm va chạm; hiện tại, các cuộc gọi lại này về mặt kỹ thuật sẽ được chạy khi BẤT K body cơ thể nào va chạm với bất kỳ cơ thể nào khác.

        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);

Các cuộc gọi lại thay đổi thuộc tính "trạng thái" của con nhện, đó là một tập hợp các booleans và ghi lại bất kỳ trọng tài va chạm Nape nào để sử dụng sau này trong logic đi bộ của tôi. Họ cũng thiết lập và xóa toTimer, cho phép con nhện mất liên lạc với bề mặt cấp độ trong tối đa 100ms trước khi cho phép trọng lực thế giới giữ lại.

    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;
    }

Cuối cùng, tôi tính toán những lực nào tác dụng lên con nhện dựa trên trạng thái của nó và mối quan hệ của nó với hình học cấp độ. Tôi sẽ chủ yếu để cho các ý kiến ​​tự nói.

    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;

            }
        }
    }

Phần dính thực sự tôi tìm thấy là góc chuyển động cần phải theo hướng chuyển động mong muốn thực tế trong kịch bản nhiều điểm tiếp xúc trong đó con nhện đạt đến một góc nhọn hoặc nằm trong một thung lũng sâu. Đặc biệt là, do các vectơ tổng hợp của tôi cho lực bám dính, lực đó sẽ kéo AWAY từ hướng chúng ta muốn di chuyển thay vì vuông góc với nó, vì vậy chúng ta cần phải chống lại điều đó. Vì vậy, tôi cần logic để chọn một trong các điểm tiếp xúc để sử dụng làm cơ sở cho góc của vectơ chuyển động.

Một tác dụng phụ của "lực kéo" của lực bám dính là một chút do dự khi con nhện đạt đến một góc / đường cong lõm sắc nét, nhưng đó thực sự là loại thực tế từ quan điểm nhìn và cảm nhận, trừ khi nó gây ra vấn đề trên đường để nó như vậy Nếu tôi cần, tôi có thể sử dụng một biến thể của phương pháp này để tính toán lực bám dính.

    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;
    }

Logic này là khá "hoàn hảo", cho đến nay nó dường như đang làm những gì tôi muốn nó làm. Tuy nhiên, có một vấn đề về mỹ phẩm kéo dài, đó là nếu tôi cố gắng căn chỉnh đồ họa của con nhện với độ bám dính hoặc lực di chuyển, tôi thấy rằng con nhện cuối cùng "nghiêng" theo hướng di chuyển, sẽ ổn thôi nếu anh ta là một vận động viên chạy nước rút hai chân nhưng anh ta thì không, và các góc rất dễ bị biến đổi trong địa hình, do đó, con nhện giật mình khi đi qua vết va chạm nhẹ nhất. Tôi có thể theo đuổi một biến thể của giải pháp Byte56, lấy mẫu phong cảnh gần đó và lấy trung bình các góc đó, để làm cho hướng của con nhện mượt mà và chân thực hơn.


1
Tốt lắm, cảm ơn vì đã đăng chi tiết ở đây cho khách truy cập trong tương lai.
MichaelHouse

8

Làm thế nào về việc làm cho bất kỳ bề mặt "dính" nào mà nhân vật chạm vào sẽ tác dụng một lực dọc theo bề mặt bình thường nghịch đảo của bề mặt? Lực vẫn duy trì miễn là chúng tiếp xúc với bề mặt và ghi đè trọng lực miễn là nó hoạt động. Vì vậy, nhảy xuống trần sẽ có tác dụng dự kiến ​​là rơi xuống sàn.

Bạn có thể muốn thực hiện một số tính năng khác để làm cho công việc này trơn tru và dễ thực hiện hơn. Ví dụ, thay vì chỉ những gì nhân vật chạm vào, hãy sử dụng một vòng tròn xung quanh nhân vật và tổng hợp các quy tắc ngược. Như hình ảnh sơn crappy này cho thấy:

nhập mô tả hình ảnh ở đây

(Khả năng giống nhện được đại diện là tài sản của Byte56)

Các đường màu xanh là các quy tắc nghịch đảo với bề mặt tại điểm đó. Đường màu xanh lá cây là lực tổng hợp được áp dụng cho con nhện. Vòng tròn màu đỏ đại diện cho phạm vi mà con nhện đang tìm kiếm thông thường để sử dụng.

Điều này sẽ cho phép một số gập ghềnh trên địa hình mà không có con nhện "mất độ bám". Kinh nghiệm về kích thước và hình dạng vòng tròn cho vấn đề đó, có thể chỉ cần sử dụng một nửa vòng tròn được định hướng với con nhện xuống, có thể chỉ là một hình chữ nhật bao quanh chân.

Giải pháp này cho phép bạn tiếp tục bật vật lý mà không cần phải xử lý các đường dẫn cụ thể mà nhân vật có thể đi theo. Nó cũng sử dụng thông tin khá dễ dàng để có được và diễn giải (quy tắc). Cuối cùng, nó năng động. Ngay cả việc thay đổi hình dạng của thế giới cũng dễ dàng được tính đến, vì bạn có thể dễ dàng có được các quy tắc cho bất kỳ hình học nào bạn đang vẽ.

Hãy nhớ rằng khi không có khuôn mặt nào nằm trong phạm vi của con nhện, trọng lực bình thường sẽ chiếm lấy.


Các quy tắc tổng hợp có thể sẽ giải quyết các vấn đề mà giải pháp hiện tại của tôi đang gặp phải với các góc lõm sắc nét, nhưng tôi không quen với cách bạn có được chúng trong AS3.
Eric N

Xin lỗi, tôi cũng không quen. Có thể một cái gì đó bạn cần để duy trì bản thân khi bạn tạo địa hình.
MichaelHouse

2
Tôi đã xoay sở để thực hiện ý tưởng này vì tôi có thể phát hiện các điểm tiếp xúc va chạm của Nape và tính trung bình cho chúng nếu có nhiều hơn một. Nó dường như không cần thiết cho việc di chuyển trên bề mặt phẳng hoặc lồi, nhưng nó đã giải quyết được vấn đề lớn nhất của tôi: phải làm gì khi con nhện của tôi gặp một góc nhọn. Như đã đề cập trong câu trả lời mới của tôi, tôi có thể thử một biến thể về ý tưởng này để giúp định hướng đồ họa của con nhện của tôi.
Eric N
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.