ฉันจะ จำกัด การเคลื่อนไหวของผู้เล่นลงบนพื้นผิวของวัตถุ 3 มิติโดยใช้ Unity ได้อย่างไร


16

ฉันพยายามที่จะสร้างเอฟเฟกต์คล้ายกับของ Mario Galaxy หรือ Geometry Wars 3 ที่ซึ่งผู้เล่นเดินไปรอบ ๆ แรงโน้มถ่วง "ดาวเคราะห์" ดูเหมือนว่าจะปรับตัวและพวกเขาจะไม่หลุดออกจากขอบของวัตถุเช่นเดียวกับแรงโน้มถ่วง ได้รับการแก้ไขในทิศทางเดียว

ป้อนคำอธิบายรูปภาพที่นี่
(ที่มา: gameskinny.com )

เรขาคณิต Wars 3

ฉันพยายามที่จะใช้บางสิ่งที่ใกล้เคียงกับสิ่งที่ฉันกำลังมองหาโดยใช้วิธีการที่วัตถุที่ควรมีแรงดึงดูดดึงดูดวัตถุแข็งอื่น ๆ เข้าหามัน แต่ด้วยการใช้เครื่องมือฟิสิกส์ในตัวสำหรับ Unity ใช้การเคลื่อนไหวกับ AddForce และไลค์ ฉันไม่สามารถทำให้การเคลื่อนไหวรู้สึกถูกต้อง ฉันไม่สามารถให้ผู้เล่นเคลื่อนที่เร็วพอโดยที่ผู้เล่นไม่ได้เริ่มบินออกจากพื้นผิวของวัตถุและฉันไม่สามารถหาสมดุลที่เหมาะสมของแรงที่ใช้และแรงโน้มถ่วงเพื่อรองรับสิ่งนี้ การนำไปใช้ในปัจจุบันของฉันคือการปรับตัวของสิ่งที่พบที่นี่

ฉันรู้สึกว่าวิธีแก้ปัญหาอาจจะยังคงใช้ฟิสิกส์เพื่อให้ผู้เล่นลงดินบนวัตถุหากพวกเขาจะออกจากพื้นผิว แต่เมื่อผู้เล่นได้รับการต่อสายดินแล้วจะมีวิธีที่จะสแนปอินผู้เล่นไปที่พื้นผิวและปิดฟิสิกส์และ ควบคุมผู้เล่นด้วยวิธีอื่น แต่ฉันไม่แน่ใจจริงๆ

ฉันควรใช้วิธีการแบบใดเพื่อสแน็ปอินผู้เล่นไปยังพื้นผิวของวัตถุ โปรดทราบว่าการแก้ปัญหาควรทำงานในพื้นที่ 3 มิติ (ตรงข้ามกับ 2D) และควรสามารถใช้งานได้โดยใช้ Unity รุ่นฟรี


ข้อมูลซ้ำที่เป็นไปได้: gamedev.stackexchange.com/questions/47220/…และgamedev.stackexchange.com/questions/71585/…
MichaelHouse

ฉันไม่ได้คิดจะค้นหาเดินบนกำแพง ฉันจะดูและดูว่าช่วยเหล่านี้
SpartanDonut

1
หากคำถามนี้เกี่ยวกับการทำสิ่งนี้ในรูปแบบ 3 มิติใน Unity โดยเฉพาะควรมีการแก้ไขให้ชัดเจนยิ่งขึ้น (มันจะไม่ซ้ำกันอย่างแน่นอนของที่มีอยู่แล้ว.)
Anko

นั่นเป็นความรู้สึกทั่วไปของฉันเช่นกัน - ฉันจะดูว่าฉันสามารถปรับแก้ปัญหาให้เป็นแบบ 3 มิติและโพสต์เป็นคำตอบ (หรือถ้ามีคนอื่นสามารถเอาชนะฉันไปที่หมัดฉันก็สบายดีด้วย) ฉันจะพยายามอัปเดตคำถามของฉันให้ชัดเจนยิ่งขึ้น
SpartanDonut

อาจเป็นประโยชน์: youtube.com/watch?v=JMWnufriQx4
ssb

คำตอบ:


7

ฉันจัดการเพื่อให้บรรลุสิ่งที่ฉันต้องการเป็นหลักด้วยความช่วยเหลือของโพสต์บล็อกนี้สำหรับชิ้นส่วนของปริศนาและเกิดขึ้นกับความคิดของฉันสำหรับการเคลื่อนไหวของผู้เล่นและกล้อง

Snapping Player กับพื้นผิวของวัตถุ

การตั้งค่าพื้นฐานประกอบด้วยทรงกลมขนาดใหญ่ (โลก) และทรงกลมขนาดเล็ก (ผู้เล่น) ทั้งคู่ที่มี colliders ทรงกลมติดอยู่

งานที่ทำเสร็จแล้วมีสองวิธีดังต่อไปนี้:

private void UpdatePlayerTransform(Vector3 movementDirection)
{                
    RaycastHit hitInfo;

    if (GetRaycastDownAtNewPosition(movementDirection, out hitInfo))
    {
        Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
        Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, float.PositiveInfinity);

        transform.rotation = finalRotation;
        transform.position = hitInfo.point + hitInfo.normal * .5f;
    }
}

private bool GetRaycastDownAtNewPosition(Vector3 movementDirection, out RaycastHit hitInfo)
{
    Vector3 newPosition = transform.position;
    Ray ray = new Ray(transform.position + movementDirection * Speed, -transform.up);        

    if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity, WorldLayerMask))
    {
        return true;
    }

    return false;
}

Vector3 movementDirectionพารามิเตอร์เป็นเพียงเสียงทิศทางที่เราจะได้รับการเคลื่อนย้ายผู้เล่นของเราในกรอบนี้และการคำนวณเวกเตอร์ว่าในขณะที่จบลงด้วยการที่ค่อนข้างง่ายในตัวอย่างนี้เป็นบิตหากินสำหรับผมที่จะคิดออกในตอนแรก เพิ่มเติมในภายหลัง แต่โปรดจำไว้ว่ามันเป็นเวกเตอร์ที่ทำให้เป็นมาตรฐานในทิศทางที่ผู้เล่นเคลื่อนไหวเฟรมนี้

สิ่งแรกที่เราทำคือตรวจสอบว่ามีรังสีเกิดขึ้นที่ตำแหน่งสมมุติในอนาคตที่มุ่งไปยังผู้เล่นแบบเวกเตอร์ (-transform.up) กระทบโลกโดยใช้ WorldLayerMask ซึ่งเป็นคุณสมบัติ LayerMask สาธารณะของสคริปต์ หากคุณต้องการการชนที่ซับซ้อนมากขึ้นหรือหลายเลเยอร์คุณจะต้องสร้างเลเยอร์มาสก์ของคุณเอง หาก raycast พบสิ่งที่ hitInfo ประสบความสำเร็จจะใช้ในการดึงจุดปกติและจุด Hit เพื่อคำนวณตำแหน่งใหม่และการหมุนของผู้เล่นซึ่งควรจะอยู่บนวัตถุ การชดเชยตำแหน่งของผู้เล่นอาจจำเป็นขึ้นอยู่กับขนาดและที่มาของวัตถุผู้เล่นที่มีปัญหา

ในที่สุดสิ่งนี้ได้รับการทดสอบจริง ๆ เท่านั้นและน่าจะใช้ได้กับวัตถุง่าย ๆ เช่นทรงกลมเท่านั้น ในขณะที่โพสต์บล็อกที่ฉันใช้วิธีแก้ปัญหาของฉันออกจากการแนะนำคุณอาจต้องการดำเนินการหลาย raycasts และค่าเฉลี่ยพวกเขาสำหรับตำแหน่งและการหมุนของคุณเพื่อรับการเปลี่ยนแปลงที่ดีกว่ามากเมื่อย้ายภูมิประเทศที่ซับซ้อนมากขึ้น อาจมีข้อผิดพลาดอื่น ๆ ที่ฉันไม่เคยคิดมาก่อนในตอนนี้

กล้องและการเคลื่อนไหว

เมื่อผู้เล่นติดกับพื้นผิวของวัตถุภารกิจต่อไปที่จะแก้ไขก็คือการเคลื่อนไหว ตอนแรกฉันเริ่มต้นด้วยการเคลื่อนไหวที่สัมพันธ์กับผู้เล่น แต่ฉันเริ่มพบปัญหาที่เสาของทรงกลมซึ่งทิศทางเปลี่ยนไปอย่างกะทันหันทำให้ผู้เล่นของฉันเปลี่ยนทิศทางไปเรื่อย ๆ อย่างรวดเร็วไม่ให้ฉันผ่านเสา สิ่งที่ฉันทำคือการทำให้ผู้เล่นเคลื่อนไหวสัมพันธ์กับกล้อง

สิ่งที่ทำงานได้ดีสำหรับความต้องการของฉันคือการมีกล้องที่ติดตามผู้เล่นอย่างเคร่งครัดตามตำแหน่งของผู้เล่นเท่านั้น แม้ว่ากล้องจะหมุนในทางเทคนิคการกดขึ้นจะเลื่อนผู้เล่นไปทางด้านบนของหน้าจอลงไปทางด้านล่างและไปทางซ้ายและขวา

เมื่อต้องการทำสิ่งนี้จะมีการดำเนินการต่อไปนี้กับกล้องที่มีวัตถุเป้าหมายคือเครื่องเล่น:

private void FixedUpdate()
{
    // Calculate and set camera position
    Vector3 desiredPosition = this.target.TransformPoint(0, this.height, -this.distance);
    this.transform.position = Vector3.Lerp(this.transform.position, desiredPosition, Time.deltaTime * this.damping);

    // Calculate and set camera rotation
    Quaternion desiredRotation = Quaternion.LookRotation(this.target.position - this.transform.position, this.target.up);
    this.transform.rotation = Quaternion.Slerp(this.transform.rotation, desiredRotation, Time.deltaTime * this.rotationDamping);
}

ในที่สุดเมื่อต้องการย้ายผู้เล่นเราใช้ประโยชน์จากการแปลงของกล้องหลักเพื่อให้มีการควบคุมของเราเลื่อนขึ้นลงย้ายลง ฯลฯ และที่นี่เราเรียก UpdatePlayerTransform ซึ่งจะได้ตำแหน่งของเรา snapped กับวัตถุโลก

void Update () 
{        
    Vector3 movementDirection = Vector3.zero;
    if (Input.GetAxisRaw("Vertical") > 0)
    {
        movementDirection += cameraTransform.up;
    }
    else if (Input.GetAxisRaw("Vertical") < 0)
    {
        movementDirection += -cameraTransform.up;
    }

    if (Input.GetAxisRaw("Horizontal") > 0)
    {
        movementDirection += cameraTransform.right;
    }
    else if (Input.GetAxisRaw("Horizontal") < 0)
    {
        movementDirection += -cameraTransform.right;
    }

    movementDirection.Normalize();

    UpdatePlayerTransform(movementDirection);
}

ในการใช้กล้องที่น่าสนใจยิ่งขึ้น แต่การควบคุมให้ใกล้เคียงกับสิ่งที่เรามีในที่นี้คุณสามารถใช้กล้องที่ไม่ได้แสดงผลหรือวัตถุจำลองอื่น ๆ เพื่อสร้างการเคลื่อนไหวจากนั้นใช้กล้องที่น่าสนใจเพื่อแสดงว่า คุณต้องการให้เกมดูเป็นอย่างไร สิ่งนี้จะช่วยให้การเปลี่ยนกล้องดีในขณะที่คุณเดินไปรอบ ๆ วัตถุโดยไม่ทำลายการควบคุม


3

ฉันคิดว่าความคิดนี้จะทำงาน:

เก็บสำเนาฝั่งดาวเคราะห์ของซีพียูไว้ การมีจุดยอดตาข่ายยังหมายความว่าคุณมีเวกเตอร์ปกติสำหรับแต่ละจุดบนดาวเคราะห์ จากนั้นปิดการใช้งานแรงโน้มถ่วงอย่างสมบูรณ์สำหรับทุกหน่วยงานแทนที่จะใช้แรงในทิศทางตรงกันข้ามของเวกเตอร์ปกติ

ทีนี้ขึ้นอยู่กับจุดที่ควรคำนวณเวกเตอร์ปกติของดาวเคราะห์?

คำตอบที่ง่ายที่สุด (ซึ่งฉันค่อนข้างมั่นใจว่าใช้ได้ดี) คือการประมาณเช่นเดียวกับวิธีการของนิวตัน : เมื่อวัตถุวางไข่ครั้งแรกคุณจะรู้ตำแหน่งเริ่มต้นทั้งหมดบนดาวเคราะห์ ใช้ตำแหน่งเริ่มต้นนั้นเพื่อกำหนดupเวกเตอร์ของแต่ละวัตถุ แรงโน้มถ่วงที่เห็นได้ชัดจะอยู่ในทิศทางตรงกันข้าม (ต่อdown) ในเฟรมถัดไปก่อนที่จะใช้แรงโน้มถ่วงให้ทำการฉายแสงจากตำแหน่งใหม่ของวัตถุไปยังdownเวกเตอร์เก่า ใช้จุดตัดของรังสีนั้นกับดาวเคราะห์เพื่อเป็นข้อมูลอ้างอิงใหม่ในการพิจารณาupเวกเตอร์ กรณีที่รังสีหายากไม่มีอะไรแปลว่ามีอะไรผิดปกติอย่างน่ากลัวและคุณควรย้ายวัตถุของคุณกลับไปยังตำแหน่งที่อยู่ในกรอบก่อนหน้า

โปรดทราบด้วยว่าการใช้วิธีนี้ผู้เล่นคนต่อไปจะมาจากโลกและยิ่งการประมาณนั้นแย่ลงเท่าใด ดังนั้นจึงเป็นการดีกว่าที่จะใช้ที่ไหนสักแห่งรอบ ๆ เท้าของผู้เล่นแต่ละคนเป็นจุดกำเนิดของพวกเขา ฉันเดา แต่ฉันคิดว่าการใช้เท้าเป็นจุดเริ่มต้นก็จะทำให้จัดการและนำทางเครื่องเล่นได้ง่ายขึ้น


บันทึกล่าสุด: เพื่อผลลัพธ์ที่ดีกว่าคุณสามารถทำสิ่งต่อไปนี้ติดตามการเคลื่อนไหวของผู้เล่นในแต่ละเฟรม (เช่นใช้current_position - last_position) จากนั้นหนีบmovement_vectorเพื่อให้ความยาวของวัตถุupเท่ากับศูนย์ ลองเรียกเวกเตอร์ใหม่reference_movementนี้ ย้ายก่อนหน้าreference_pointด้วยreference_movementและใช้จุดใหม่นี้เป็นจุดกำเนิดการติดตามรังสี หลังจาก (และถ้า) รังสีreference_pointพุ่งเข้าหาดาวเคราะห์ให้ย้ายไปที่จุดนั้น สุดท้ายคำนวณupเวกเตอร์ใหม่จากอันใหม่นี้reference_pointนี้

โค้ดหลอกบางตัวที่จะหาผลรวม:

update_up_vector(player:object, planet:mesh)
{
    up_vector        = normalize(planet.up);
    reference_point  = ray_trace (object.old_position, -up_vector, planet);
    movement         = object.position - object.old_position;
    movement_clamped = movement - up_vector * dot (movement, up_vector);

    reference_point  += movement_clamped;
    reference_point  = ray_trace (reference_point, -up_vector, planet);
    player.up        = normal_vector(mesh, reference_point);
}

2

โพสต์นี้อาจมีประโยชน์ ส่วนสำคัญของมันคือคุณไม่ได้ใช้ตัวควบคุมตัวละคร แต่สร้างด้วยตัวคุณเองโดยใช้เครื่องมือฟิสิกส์ จากนั้นคุณใช้บรรทัดฐานที่ตรวจพบใต้โปรแกรมเล่นเพื่อจัดแนวพวกเขาไปที่พื้นผิวของตาข่าย

นี่เป็นภาพรวมที่ดีของเทคนิค มีแหล่งข้อมูลมากมายด้วยคำค้นหาบนเว็บเช่น "ความสามัคคีเดินบนวัตถุ 3 มิติมาริโอกาแล็กซี่"

นอกจากนี้ยังมีโครงการสาธิตจากเอกภาพ 3.x ที่มีเครื่องยนต์เดินพร้อมทหารและสุนัขและหนึ่งในฉาก มันแสดงให้เห็นถึงการเดินบนวัตถุ 3 มิติสไตล์กาแล็กซี่ มันเรียกว่าระบบการเคลื่อนที่โดย runevision


1
ภาพรวมการสาธิตครั้งแรกนั้นใช้งานได้ไม่ดีนักและแพ็คเกจ Unity มีข้อผิดพลาดในการสร้าง ฉันจะดูว่าฉันสามารถทำงานนี้ได้หรือไม่ แต่คำตอบที่สมบูรณ์ยิ่งขึ้นก็จะได้รับการชื่นชม
SpartanDonut

2

การหมุน

ชำระเงินคำตอบสำหรับคำถามนี้บน answers.unity3d.com (จริง ๆ แล้วถามด้วยตัวเอง) อ้างถึง:

ภารกิจแรกคือการหาเวกเตอร์ที่กำหนดขึ้น จากรูปวาดของคุณคุณสามารถทำได้สองวิธี คุณสามารถปฏิบัติต่อดาวเคราะห์ในรูปทรงกลมและใช้งานได้ (object.position - planet.position) วิธีที่สองคือการใช้ Collider.Raycast () และใช้ 'hit.normal' ที่ได้รับคืน

นี่คือรหัสที่เขาแนะนำให้ฉัน:

var up : Vector3 = transform.position - planet.position;
transform.rotation = Quaternion.FromToRotation(transform.up, up) * transform.rotation;

เรียกว่าทุกการอัพเดทและคุณควรทำให้มันใช้งานได้ (โปรดทราบว่ารหัสอยู่ในUnityScript )
รหัสเดียวกันในC # :

Vector3 up = transform.position - planet.position;
transform.rotation = Quaternion.FromToRotation(transform.up, up) * transform.rotation;

แรงดึงดูด

สำหรับแรงโน้มถ่วงคุณสามารถใช้ "เทคนิคฟิสิกส์ดาวเคราะห์" ของฉันซึ่งฉันอธิบายไว้ในคำถามของฉัน แต่มันไม่ได้ปรับให้เหมาะสมจริง ๆ มันเป็นสิ่งที่ฉันนึกถึง
ฉันขอแนะนำให้คุณสร้างระบบของคุณเองสำหรับแรงโน้มถ่วง

ที่นี่คือ การกวดวิชา Youtube โดยเซบาสเตียน Lague นั่นเป็นทางออกที่ดีมาก

แก้ไข:ในความเป็นเอกภาพไปที่แก้ไข > การตั้งค่าโครงการ > ฟิสิกส์และตั้งค่าทั้งหมดสำหรับGravityเป็น 0 (หรือลบRigidbodyออกจากวัตถุทั้งหมด) เพื่อป้องกันแรงโน้มถ่วงในตัว (ซึ่งเพิ่งดึงผู้เล่นลง) ขัดแย้งกับ โซลูชันที่กำหนดเอง (ปิดมัน)


การหมุนผู้เล่นไปยังศูนย์กลางของดาวเคราะห์ (และหมุนตามนั้น) ทำงานได้ดีสำหรับดาวเคราะห์ที่มีลักษณะเป็นทรงกลม แต่ฉันสามารถจินตนาการได้ว่ามันผิดปกติมากสำหรับวัตถุทรงกลมที่น้อยกว่าเช่นมาริโอรูปจรวดที่กระโดดขึ้นใน GIF แรก
Anko

คุณสามารถสร้างคิวบ์ที่ถูก จำกัด ให้เคลื่อนที่ไปรอบ ๆ แกน X เท่านั้นเช่นภายในวัตถุที่ไม่เป็นทรงกลมและเคลื่อนย้ายเมื่อผู้เล่นเคลื่อนที่เพื่อให้มันอยู่ใต้เครื่องเล่นเสมอและดึงผู้เล่นออกมา ลงไปที่ก้อน ลองมัน.
Daniel Kvist
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.