คุณจะย้ายสไปรต์ด้วยการเพิ่มทีละพิกเซลย่อยได้อย่างไร


11

พิกเซลเปิดหรือปิด จำนวนขั้นต่ำที่คุณสามารถเคลื่อนย้ายสไปรท์คือพิกเซลเดียว ดังนั้นวิธีที่คุณทำให้สไปรต์เคลื่อนที่ช้ากว่า 1 พิกเซลต่อเฟรม?

วิธีที่ฉันทำคือเพิ่มความเร็วให้กับตัวแปรแล้วทดสอบว่ามีค่าถึง 1 (หรือ -1) ถ้าเป็นเช่นนั้นฉันจะย้ายสไปรต์และรีเซ็ตตัวแปรเป็น 0 เช่น:

update(dt):
    temp_dx += speed * dt
    temp_dy += speed * dt

    if (temp_dx > 1)
        move sprite
        reset temp_dx to 0
    if (tempy_dy > 1)
        move sprite
        reset temp_dy to 0

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


สิ่งเดียวที่คุณทำได้คือการกรองแบบสองทาง
AturSams

หากคุณเคลื่อนไหวน้อยกว่า 1 px ต่อเฟรมมันจะดูกระตุกได้อย่างไร?

คำตอบ:


17

มีตัวเลือกมากมาย:

  1. ทำตามที่คุณทำ คุณพูดไปแล้วว่ามันดูไม่ราบรื่น มีข้อบกพร่องบางอย่างกับวิธีการปัจจุบันของคุณ สำหรับxคุณสามารถใช้สิ่งต่อไปนี้:

    tempx += speed * dt
    
    while (tempx > 0.5)
      move sprite to x+1
      tempx -= 1
    while (tempx < -0.5)
      move sprite to x-1
      tempx += 1

    ควรจะดีกว่านี้ ฉันได้เปลี่ยน if statments ให้ใช้ 0.5 เหมือนเมื่อคุณผ่าน 0.5 คุณจะเข้าใกล้ค่าถัดไปมากกว่าค่าก่อนหน้า ฉันใช้ในขณะที่ลูปเพื่ออนุญาตให้มีการเคลื่อนไหวมากกว่า 1 พิกเซลต่อขั้นตอน (ไม่ใช่วิธีที่ดีที่สุดที่จะทำ แต่ทำเพื่อคอมแพคและโค้ดที่อ่านได้) สำหรับวัตถุที่เคลื่อนที่ช้ามันจะยังคงน่ากลัวเพราะเราไม่ได้จัดการกับปัญหาพื้นฐานของการจัดตำแหน่งพิกเซล

  2. มีกราฟิกมากมายสำหรับ sprite ของคุณและใช้กราฟิกที่แตกต่างกันโดยขึ้นอยู่กับ subpixel offset ในการทำพิกเซลย่อยให้เรียบใน x เท่านั้นคุณสามารถสร้างกราฟิกสำหรับสไปรต์ของคุณได้ที่x+0.5และถ้าตำแหน่งอยู่ระหว่างx+0.25และx+0.75ให้ใช้สไปรต์นี้แทนที่จะเป็นต้นฉบับของคุณ หากคุณต้องการตำแหน่งที่ละเอียดกว่าเพียงแค่สร้างกราฟิกย่อยพิกเซลเพิ่มเติม หากคุณทำสิ่งนี้xและyจำนวนการเรนเดอร์ของคุณสามารถบอลลูนได้อย่างรวดเร็วเนื่องจากจำนวนนั้นจะถูกสแควร์กับจำนวนช่วงเวลา: การเว้นวรรค0.5จะต้องใช้การเรนเดอร์ 4 ครั้ง0.25จำเป็นต้องมี 16

  3. Supersample นี่เป็นวิธีการสร้างภาพที่ขี้เกียจ (และอาจมีราคาแพงมาก) ด้วยความละเอียดพิกเซลย่อย โดยพื้นฐานแล้วให้เพิ่มความละเอียดเป็นสองเท่า (หรือมากกว่า) ที่คุณสร้างฉากของคุณจากนั้นลดขนาดลงที่รันไทม์ ฉันอยากจะแนะนำที่นี่เพราะประสิทธิภาพอาจลดลงอย่างรวดเร็ว วิธีที่รวดเร็วกว่าในการทำเช่นนี้ก็คือการยกตัวอย่างสไปรต์ของคุณและลดระดับลงขณะรันไทม์

  4. ตามที่แนะนำโดยZehelvionอาจเป็นไปได้ว่าแพลตฟอร์มที่คุณใช้รองรับสิ่งนี้อยู่แล้ว หากคุณได้รับอนุญาตให้ระบุพิกัด x และ y ที่ไม่ใช่จำนวนเต็มอาจมีตัวเลือกในการเปลี่ยนการกรองพื้นผิว เพื่อนบ้านที่ใกล้ที่สุดมักจะเป็นค่าเริ่มต้นและสิ่งนี้จะทำให้เกิดการเคลื่อนไหว "กระตุก" การกรองอื่น ๆ (แบบเส้นตรง / ลูกบาศก์) จะส่งผลที่ราบรื่นกว่ามาก

ตัวเลือกระหว่าง 2. 3 และ 4 ขึ้นอยู่กับวิธีการใช้งานกราฟิกของคุณ รูปแบบบิตแมปของกราฟิกที่เหมาะกับการเรนเดอร์ล่วงหน้าได้ดีกว่าในขณะที่สไตล์เวกเตอร์อาจเหมาะกับสไปรท์ตัวอย่าง หากระบบของคุณรองรับ 4. อาจเป็นหนทางไป


ทำไมจึงต้องเปลี่ยนค่า threshold เป็น 0.5 ในการเลือก 1 ฉันยังไม่เข้าใจว่าทำไมคุณย้ายข้อความไปเป็นลูปสักครู่ ฟังก์ชั่นอัพเดทจะถูกเรียกหนึ่งครั้งต่อหนึ่งขีด
bzzr

1
เป็นคำถามที่ดี - ฉันได้ชี้แจงคำตอบตามความคิดเห็นของคุณ
Jez

การสุ่มตัวอย่างแบบ Supersampling อาจเป็น "ขี้เกียจ" แต่ในหลายกรณีค่าใช้จ่ายด้านประสิทธิภาพของการ oversampling 2x หรือ 4x จะไม่เป็นปัญหาโดยเฉพาะอย่างยิ่งถ้ามันอนุญาตให้การเรนเดอร์ด้านอื่น ๆ
supercat

ในเกมที่มีงบประมาณ จำกัด ฉันมักจะเห็น 3 ตัวเลือกในการตั้งค่ากราฟิกเป็นการต่อต้านนามแฝง
เควิน

1
@ เควิน: จริง ๆ แล้วคุณไม่ทำ เกมส่วนใหญ่ใช้การสุ่มหลายตัวอย่างซึ่งค่อนข้างแตกต่างกัน นอกจากนี้ยังมีการใช้ตัวแปรอื่น ๆ ด้วยเช่นความครอบคลุมที่หลากหลายและตัวอย่างความลึก การสุ่มตัวอย่างแบบซูเปอร์แทบจะไม่เคยทำได้เนื่องจากค่าใช้จ่ายในการประมวลผลส่วนย่อย
imallett

14

วิธีหนึ่งที่เกมเก่า ๆ ของ Skool แก้ไข (หรือซ่อน) ปัญหานี้คือการทำให้สไปรต์เคลื่อนไหว

นั่นคือถ้าสไปรต์ของคุณกำลังเคลื่อนไหวน้อยกว่าหนึ่งพิกเซลต่อเฟรม (หรือโดยเฉพาะอย่างยิ่งถ้าอัตราส่วนพิกเซล / เฟรมจะเป็นสิ่งที่แปลกเช่นพิกเซล 2 ใน 3 เฟรม) คุณสามารถซ่อนความกระตุกด้วยการทำให้nวนภาพเคลื่อนไหวเฟรมที่เหนือเฟรมnเหล่านั้นจบลงด้วยการย้ายเทพดาด้วยk < nพิกเซลบางส่วน

ประเด็นก็คือว่าตราบใดที่เทพดาเสมอย้ายในบางวิธีในแต่ละเฟรมมีไม่เคยจะเป็นกรอบเดียวใด ๆ ที่เทพดาทั้งหมดจะก็ "กระตุก" ไปข้างหน้า


ฉันไม่สามารถหาเทพดาจริงจากวิดีโอเกมเก่าเพื่ออธิบายสิ่งนี้ (แม้ว่าฉันคิดว่าเช่นภาพเคลื่อนไหวขุดบางส่วนจากLemmingsเป็นเช่นนั้น) แต่ปรากฎว่ารูปแบบ"เครื่องร่อน"จากGame of Life ของ Conwayทำให้ ภาพประกอบที่ดีมาก:

ป้อนคำอธิบายรูปภาพที่นี่
แอนิเมชันโดยKieff / Wikimedia Commonsซึ่งใช้ภายใต้ใบอนุญาตCC-By-SA 3.0

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


2
นี่เป็นวิธีการที่ยอดเยี่ยมหากความเร็วคงที่หรือน้อยกว่า 1/2 พิกเซลต่อเฟรมหรือหากภาพเคลื่อนไหวมีขนาดใหญ่พอและเร็วพอที่จะบดบังความแปรปรวนของการเคลื่อนไหว
supercat

นี่เป็นวิธีการที่ดีแม้ว่าหากกล้องต้องทำตามสไปรต์คุณจะยังคงพบปัญหาเพราะมันจะไม่เคลื่อนไหวค่อนข้างราบรื่น
Elden Abob

6

ตำแหน่งของสไปรต์ควรเก็บไว้เป็นปริมาณจุดลอยตัวและปัดเศษเป็นจำนวนเต็มก่อนแสดงเท่านั้น

คุณยังสามารถรักษาสไปรต์ได้ที่ความละเอียดสูงสุดและดาวน์สไปรต์ตัวอย่างก่อนแสดง หากคุณรักษาสไปรต์ที่ความละเอียดการแสดงผล 3x คุณจะมีสไปรท์ที่แตกต่างกัน 9 ตัวขึ้นอยู่กับตำแหน่งพิกเซลย่อยของสไปรท์


3

ทางออกที่แท้จริงเท่านั้นที่นี่คือการใช้การกรอง bilinear แนวคิดก็คือเพื่อให้ GPU คำนวณค่าของแต่ละพิกเซลโดยอิงจากสไปรต์พิกเซลสี่ตัวที่ซ้อนทับกัน มันเป็นเทคนิคทั่วไปและมีประสิทธิภาพ คุณเพียงแค่วางเทพดาบน 2d-plain (บิลบอร์ด) เป็นพื้นผิว; จากนั้นใช้ GPU เพื่อแสดงผลที่ราบเหล่านี้ วิธีนี้ใช้งานได้ดี แต่คาดว่าจะได้ผลลัพธ์ที่ค่อนข้างพร่ามัวและสูญเสียรูปแบบ 8 บิตหรือ 16 บิตหากคุณตั้งเป้าไว้

ข้อดี:

มีการใช้งานโซลูชันที่อิงกับฮาร์ดแวร์อย่างรวดเร็ว

ข้อเสีย:

การสูญเสียความเที่ยงตรง 8-bit / 16-bit


1

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

อย่างไรก็ตามยังมีอีกวิธีหนึ่งในการทำสิ่งที่คุณกำลังทำอยู่ แต่ก็สะบัดน้อยกว่า: จุดคงที่ ค่าตำแหน่ง 32 บิตสามารถแทนค่า 0 ถึง 4,294,967,295 ถึงแม้ว่าหน้าจอของคุณจะมีพิกเซลน้อยกว่า 4k ตามแนวแกน ดังนั้นใส่ "เลขฐานสองจุด" ที่คงที่ (โดยการเปรียบเทียบกับจุดทศนิยม) ที่อยู่ตรงกลางและคุณสามารถแสดงตำแหน่งของพิกเซลจาก 0-65536 ด้วยความละเอียดพิกเซลย่อยอีก 16 บิต จากนั้นคุณสามารถเพิ่มตัวเลขได้ตามปกติคุณต้องจำไว้ว่าต้องแปลงโดยเลื่อน 16 บิตไปทางขวาเมื่อใดก็ตามที่แปลงเป็นพื้นที่หน้าจอหรือเมื่อคุณคูณสองตัวเลขเข้าด้วยกัน

(ฉันเขียนเอนจิ้น 3 มิติในจุดคงที่ 16 บิตย้อนกลับไปในวันที่ฉันมี Intel 386 ที่ไม่มีหน่วยจุดลอยตัวมันบรรลุความเร็วที่ทำให้ไม่เห็นโพลีตันของโป่งพองที่มีสีโป่งพองได้ 200 อันต่อเฟรม)


1

นอกเหนือจากคำตอบอื่น ๆ ที่นี่คุณสามารถใช้ความคิดบางอย่าง การทำ Dithering เป็นจุดที่ขอบของพิกเซลของวัตถุนั้นมีสีอ่อนลง / เข้มขึ้นเพื่อให้ตรงกับพื้นหลังเพื่อให้ขอบที่นุ่มนวลขึ้น ตัวอย่างเช่นสมมติว่าคุณมีสี่เหลี่ยมจัตุรัสขนาด 4 พิกเซลซึ่งจะเป็นค่าประมาณ:

OO
OO

หากคุณต้องการย้าย 1/2 พิกเซลนี้ไปทางขวาไม่มีอะไรจะย้ายจริงๆ แต่ด้วยความงี่เง่าบางอย่างคุณสามารถทำให้ภาพลวงตาของการเคลื่อนไหวเล็กน้อย พิจารณา O เป็นสีดำและ o เป็นสีเทาดังนั้นคุณอาจทำ:

oOo
oOo

ในหนึ่งเฟรมและ

 OO
 OO

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

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