เอฟเฟกต์สไปรต์และข้อความ

เงา เส้นขอบ เรืองแสง และเบลอ บนสไปรต์ 2D และบนข้อความ พร้อมต้นทุนของแต่ละอย่าง ผลที่มีต่อสไปรต์ที่กำลังเคลื่อนไหว และสูตรที่มันมาแทนที่

สิ่งนี้มีไว้เพื่ออะไร#

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

สี่คำสั่งทำสิ่งนี้ให้คุณ บนสไปรต์และบนข้อความ:

hero = LoadSprite("hero.png")
SpritePosition(hero, 160, 120)

SpriteShadow(hero, 4, 4, 6, 128)        ' offset x, offset y, softness, opacity
SpriteOutline(hero, 2, 255, 255, 255)   ' thickness, colour
SpriteGlow(hero, 0, 240, 255, 8)        ' colour, size

DO
  Sync()
LOOP

ไม่มีอะไรอื่นให้ต้องดูแลรักษา ย้ายสไปรต์ ปรับขนาด พลิกมัน เล่นแอนิเมชันของมัน: เอฟเฟกต์จะตามไปเอง

สี่เอฟเฟกต์#

Shadow#

SpriteShadow(spriteID, offsetX, offsetY, softness, opacity)
SpriteShadowColor(spriteID, red, green, blue)

offsetX และ offsetY เป็นหน่วยหน้าจอของคุณเอง นับแบบเดียวกับ SpritePosition: ค่า Y เป็นบวกคือ ลง softness กระจายเงาออก opacity มีค่าตั้งแต่ 0 ถึง 255 สีเป็นสีดำเว้นแต่คุณจะระบุอย่างอื่น

SpriteShadow(hero, 3, 5, 4, 110)
SpriteShadowColor(hero, 20, 0, 40)      ' a cold shadow, not a grey one

เงาที่มี opacity เป็น 0 คือไม่มีเงา นั่นคือวิธีปิดมัน

Outline#

SpriteOutline(spriteID, thickness, red, green, blue)

เส้นขอบตาม ซิลูเอตต์ ของสไปรต์ ไม่ใช่ตามกรอบสี่เหลี่ยมที่มันอาศัยอยู่: PNG แบบโปร่งใสจะได้เส้นขอบรอบรูปวาด ไม่ใช่รอบไฟล์ ความหนาเป็นหน่วยหน้าจอ และ 1 ถึง 3 คือช่วงที่ใช้งานได้จริง เกินกว่านั้นแปดทิศทางที่เส้นขอบถูกสร้างขึ้นจะเริ่มเห็นเป็น แปดพู

SpriteOutline(hero, 2, 255, 255, 255)   ' the readable-on-anything outline
SpriteOutline(hero, 0, 0, 0, 0)         ' off

Glow#

SpriteGlow(spriteID, red, green, blue, size)

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

SpriteGlow(pickup, 255, 210, 0, 10)     ' this one is worth walking to

Blur#

SpriteBlur(spriteID, radius)

Blur คือตัวที่แตกต่างออกไป อีกสามตัวถูกวาด รอบ สไปรต์ ส่วน blur แทนที่ภาพ ของสไปรต์เองด้วยสำเนาที่เบลอแล้ว นั่นหมายความว่ามีสองสิ่งที่ควรรู้:

  • มันไม่มีต้นทุนต่อเฟรม (ภาพที่เบลอถูกคำนวณเพียงครั้งเดียว) และ
  • การเบลอถูกตัดที่ขอบของภาพ ภาพวาดที่แตะขอบไฟล์ PNG ของมันจะเสียส่วนของรัศมี ที่ควรจะล้นออกไปนอกไฟล์ เว้นระยะขอบโปร่งใสไว้สักสองสามพิกเซลในไฟล์ ถ้าคุณ วางแผนจะเบลอมัน

SpriteColor ยังคงทำงานได้กับสไปรต์ที่เบลอแล้ว

ปิดทุกอย่าง แล้วเปิดกลับมา#

SpriteEffects(spriteID, 0)   ' all four off
SpriteEffects(spriteID, 1)   ' all four back, exactly as they were

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

สี่อย่างเดียวกันนี้บนข้อความ#

score = CreateText("SCORE 0")
TextPositionSet(score, 10, 10)
TextSizeSet(score, 6)

TextOutline(score, 3, 0, 0, 0)          ' readable on any background
TextShadow(score, 2, 2, 4, 160)
TextGlow(score, 255, 80, 0, 6)
TextBlur(score, 0)
TextEffects(score, 1)

ลายเซ็นตรงกับของสไปรต์ทุกประการ จึงไม่มีอะไรใหม่ต้องเรียนรู้ มีสองข้อแตกต่างด้าน พฤติกรรม:

  • glow ชนะ softness ของ shadow ทั้งสองถูกวาดด้วยกลไกเดียวกัน และ glow ที่ไม่มี softness จะมองไม่เห็น ในขณะที่ shadow ที่ไม่มี softness ก็ยังคงเป็นเงา อยู่ เมื่อคุณขอทั้งสองอย่าง glow จะได้ softness ไป
  • สิ่งเหล่านี้ใช้ได้กับข้อความแบบเวกเตอร์เท่านั้น ไม่ใช่ข้อความแบบ bitmap-font ข้อความที่วาดด้วย TextFontImage คือการประกอบกันของสไปรต์ คำสั่งเหล่านี้จะถูก เพิกเฉยอย่างเงียบ ๆ กับมัน ให้ใช้เอฟเฟกต์ของสไปรต์กับสไปรต์เหล่านั้นแทนในกรณีนั้น

ตัวอย่างที่ใช้งานได้จริง: อ่านได้บนพื้นหลังทุกแบบ#

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

CreateText(1, "SCORE 0")
TextPositionSet(1, 10, 10)
TextSizeSet(1, 8)
TextColorSet(1, 255, 255, 255, 255)
TextOutline(1, 3, 0, 0, 0)

hero = LoadSprite("hero.png")
SpriteName(hero, "hero")
SpriteShadow(hero, 4, 6, 5, 120)
SpriteOutline(hero, 2, 255, 255, 255)

points = 0
DO
  points = points + 1
  TextString(1, "SCORE " + Str(points))
  Sync()
LOOP

การอ่านค่าเอฟเฟกต์กลับมา#

พารามิเตอร์ทุกตัวที่คุณเขียนมีตัวอ่านค่า เหมือนที่ SpriteColor มี SpriteColorRed นั่นคือกฎของภาษาถิ่นนี้ และคำสั่งเหล่านี้ก็ทำตามกฎเดียวกัน

IF SpriteEffectsEnabled(hero) = 1
  SpriteEffects(hero, 0)
ENDIF

alpha = SpriteShadowAlpha(hero)      ' 0 to 255
size  = SpriteGlowSize(hero)         ' 0 means no glow

ชุดเต็มทั้งหมด ต่อกลุ่ม: SpriteShadowOffsetX, SpriteShadowOffsetY, SpriteShadowBlur, SpriteShadowAlpha, SpriteShadowColorRed, SpriteShadowColorGreen, SpriteShadowColorBlue, SpriteGlowRed, SpriteGlowGreen, SpriteGlowBlue, SpriteGlowSize, SpriteOutlineThickness, SpriteOutlineRed, SpriteOutlineGreen, SpriteOutlineBlue, SpriteBlurRadius, SpriteEffectsEnabled บวกกับสิบเจ็ด ตัวเดียวกันที่มีคำนำหน้า Text

สองกฎที่ควรรู้:

  • สไปรต์ที่ไม่เคยถูกกำหนดเอฟเฟกต์เลยจะรายงานตัวว่าเปิดอยู่ นั่นคือสถานะ เริ่มต้น การตอบ 0 จะบอกโปรแกรมว่าเอฟเฟกต์ของมันถูกตัดออกไปแล้ว ก่อนที่จะมีการ ตั้งค่าใด ๆ เลย
  • การถามถึงสไปรต์ที่ไม่มีอยู่จริงจะตอบ 0 ไม่ใช่หยุดโปรแกรม นั่นคือวิธีที่ ภาษาถิ่นนี้ปฏิบัติต่อคำถามทุกแห่ง: SpriteHit ตอบ 0 เมื่อไม่มีอะไรแตะกัน และ โปรแกรมอ่านค่า 0 นั้นโดยไม่ต้องทดสอบมัน

ต้นทุนของมัน#

Shadow, outline และ glow ถูก วาด ดังนั้นมันมีต้นทุนเป็น draw call: หนึ่งครั้ง สำหรับเงา (สามครั้งเมื่อเป็นเงานุ่ม) สี่ครั้งสำหรับ glow แปดครั้งสำหรับ outline หน้าจอที่มีสไปรต์แบบมีเส้นขอบสองร้อยตัวคือหน้าจอที่วาดสี่เหลี่ยมหนึ่งพันแปดร้อยชิ้น และมันจะแสดงผลชัดเจน สิ่งเหล่านี้มีไว้สำหรับสิ่งไม่กี่อย่างที่ผู้เล่นควรมองไปที่: ตัวละคร ไอเทม หน่วยที่ถูกเลือก

Blur ไม่มีต้นทุนต่อเฟรม

กฎที่ใช้ได้จริง: ใส่เอฟเฟกต์กับสิ่งที่เคลื่อนไหวและสำคัญ ไม่ใส่กับพื้นหลัง

สิ่งที่ข้ามไปยังเกมที่ส่งออกแล้ว#

เป้าหมายShadow, outline, glowBlurเอฟเฟกต์ข้อความ
ตัวแก้ไขใช่ใช่ใช่
ส่งออก HTML5ใช่ใช่ใช่
Native และ wasm (Bevy)ใช่ไม่ไม่

Shadow, outline และ glow ถูกสร้างจากสไปรต์ธรรมดา จึงเดินทางไปได้ทุกที่ที่สไปรต์ ไปได้ Blur และเอฟเฟกต์ข้อความต้องการสะพานที่เป้าหมาย Bevy ยังไม่มี: รายงานการ แปลง (conversion report) ระบุชื่อมันไว้ แทนที่จะทิ้งมันไปเงียบ ๆ ดังนั้นการบิลด์ จะบอกคุณว่ามันพกพาอะไรไปด้วยไม่ได้

จุดที่เอฟเฟกต์ไม่มี#

ไม่มี SpriteEffect(id, "glow", ...) ที่รับชื่อเอฟเฟกต์เป็นสตริง แต่ละเอฟเฟกต์เป็น คำสั่งของตัวเอง เพื่อให้พารามิเตอร์ของมันมีชื่อ มีเอกสาร และเติมคำอัตโนมัติได้ขณะพิมพ์ สำหรับสิ่งที่เกินกว่าสี่อย่างนี้ มีอีกสองคำสั่งที่รับช่วงต่อ: SpriteBlendModes(id, src, dst) สำหรับการผสมแบบบวกหรือคูณ (ไฟ เลเซอร์) และ SpriteShader(id, shaderID) เพื่อใส่ shader ที่คอมไพล์โดย LoadSpriteShader ลงบนสไปรต์ ทั้งคู่ถูกประกาศไว้ในแคตตาล็อกมาเป็นเวลานานโดยไม่มีการใช้งานจริง และ การเรียกใช้มันเคยแจ้งข้อผิดพลาด แต่ตอนนี้มันทำงานได้แล้ว