Gönderen Konu: Parallax mantığı  (Okunma sayısı 32745 defa)

0 Üye ve 1 Ziyaretçi konuyu incelemekte.

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Parallax mantığı
« : 11 Aralık 2013, 21:14:12 »
Merhabalar ilk mesajımda belirttiğim üzere kafa ütülemeye başlıyorum.

İlk parallax scroller kodumu yazmaya çalışıyorum. Hedeflerim
  • Ekranı üç banda ayıracağım. en üst bant en yavaş, orta bant hızlı alt kısım ise en hızlı olacak.
  • Tek yönlü sağdan sola kaydırma yapacağım
  • Scroll hiç durmayacak
  • Ayrı bir yede tutacağım bir değere göre scroll hızı değişecek

Bunu başarmak için timer ve raster kesmelerini beraber kullanmayı düşündüm. kaydırma hızını bir hafıza adresine yazacağım. Timer IRQ hep aynı hızda çalışacak ve kaydırma hızı verisine göre kaydırma zamanına kadar verecek. (Başka bir hafıza adresine değer yazacak.) eğer kaydırma zamanı ise raster IRQ oluştuğunda atlanan subrutin kaydırma yapacak.

Mantıklı olan pratikte uygulanan sistem bu mudur? Kurguladığım algoritmada bir mantık hatası var mı? Timer IRQ'yu unutup herşeyi raster'a bağlamak daha mı mantıklı? Kafamı kurcalayan sorunlar bunlar.
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı Ref

  • Yönetici
  • Özgür Retrocu
  • *
  • İleti: 3093
  • Advanced User Simulator
    • ae unutmadan
Ynt: Parallax mantığı
« Yanıtla #1 : 12 Aralık 2013, 09:55:07 »
platform hangisi c64 mü? Her donanımın kendine has yöntemleri var.

Çevrimdışı witchdoktor

  • RAAT
  • Normalleşmiş Retroman
  • *
  • İleti: 757
Ynt: Parallax mantığı
« Yanıtla #2 : 12 Aralık 2013, 10:10:50 »
Bence timer IRQ olayına hiç girme, tüm zamanlamayı rasterline üzerinden yap.

3 ayrı kademede kaydırma için 3 ayrı ekranı splitscreen olarak kullanıp, ilgili rasterline'da uygun ekranı aktifleyip bu ekranın da sadece görünen kısımlarını değişken hızlarda olarak kaydırabilirsin. Bellekte de ekran için tahsis edilen 1K'lık alanların görünen alan dışındaki kısımlarını sprite görüntüleri veya her türlü veri için kullanabilirsin.

Bu durumda bu alanlar üzerinde sadece sprite'ları kullanabilirsin.

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Parallax mantığı
« Yanıtla #3 : 12 Aralık 2013, 11:53:38 »
@ref:

Biraz fazla hızlı bir giriş oldu galiba. Ne üzerinde çalıştığımdan hiç bahsetmemişim. C64 için bir yarış oyunu projem var. Yani evet C64'ten bahsediyoruz.

@witchdoktor:

Bu timer olayı çok temel bir karar galiba. Anladığım kadarıyla olsa da olur olmasa da. Timer veya raster ikisinden birini seçip o yolda devam etmek gerekiyor gibi. Aynı hadiseyle actionscript öğrenirken de yaşamıştım. AS'de de timer ve refresh event'lerini kullanan oyunlar var. İkisiyle de aynı sonuca varılıyordu.

İlerde elime ayağıma dolaşmaması için timer olayını rafa kaldırayım olmazsa, tabi bu seferde delay rutinleri girecek işin içine.
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı witchdoktor

  • RAAT
  • Normalleşmiş Retroman
  • *
  • İleti: 757
Ynt: Parallax mantığı
« Yanıtla #4 : 12 Aralık 2013, 12:03:36 »
@ref:

Biraz fazla hızlı bir giriş oldu galiba. Ne üzerinde çalıştığımdan hiç bahsetmemişim. C64 için bir yarış oyunu projem var. Yani evet C64'ten bahsediyoruz.

@witchdoktor:

Bu timer olayı çok temel bir karar galiba. Anladığım kadarıyla olsa da olur olmasa da. Timer veya raster ikisinden birini seçip o yolda devam etmek gerekiyor gibi. Aynı hadiseyle actionscript öğrenirken de yaşamıştım. AS'de de timer ve refresh event'lerini kullanan oyunlar var. İkisiyle de aynı sonuca varılıyordu.

İlerde elime ayağıma dolaşmaması için timer olayını rafa kaldırayım olmazsa, tabi bu seferde delay rutinleri girecek işin içine.

Timer IRQ ile yapacağın zamanlamalarda mutlaka rasterline veya frame kayıpları olacaktır. Tüm IRQ'ları işlerken diğer IRQ'ları kapatacağın için, çakışan durumlarda kaymalar illa ki olacak.

Tüm işlemleri rasterline kesmelerinde gerçekleştirip programın ana döngüsünü sonsuz boş döngü olarak kullanabileceğin gibi, hiç IRQ kullanmayıp tüm işleri ana döngü içinde rasterline'ları bekleyerek de gerçekleştirebilirsin. Sonuçta iş, her yapacağın işin ne kadar rasterline'lık CPU cycle'ı yiyeceğinde düğümleniyor.

Çevrimdışı Ref

  • Yönetici
  • Özgür Retrocu
  • *
  • İleti: 3093
  • Advanced User Simulator
    • ae unutmadan
Ynt: Parallax mantığı
« Yanıtla #5 : 12 Aralık 2013, 15:10:15 »
Tabii c64 ile pek alakam yok ama bildiğim kadarıyla vic'de bir scroll registeri var 0xd016'da. Ayrıca bir raster irq var, programlanabilir bir kesme isteği. Ben olsam, eğer ekranın üstteki 3 plane'i hep aynı şekilde ise, bir kere cycle sayıp tüm planelerin kaydırılmalarının kaç cycle tuttuğunu hesaplar, programlanabilir kesme isteğine göre paralax'ımı tek raster'a yerleştirdiğim kesme ile tamamlardım. Geri kalan vakte de oyun logic'ini koyardım.

Yani, örnek, diyelim ki ekranın ilk 3 karakter satırı gökyüzü, ikinci 3lü satırı tarlalar, üçüncü üçlü satırı yol kenarındaki ağaçlar olsun.

-x=1 olsun

-0. raster satırına irq kur (http://www.c64-wiki.com/index.php/Raster_interrupt )
-bitmask'ı kontrol et, bit set ise,
--d016'da x pixel kaydır, cycle say, 3 sütun geçsin.
--d016'da x pixel kaydır, cycle say, 3 sütun geçsin.
--d016'da x+x pixel kaydır, cycle say, 3 sütun geçsin.
--8 pixel kaydı ise, karakterleri kaydır.

böylece 1.katman 1 pixel, 2. katman 2 pixel, 3.katman 4 pixel kaymış olur, bence işi görür.

Vic'i 8 pixelde bir geri alman gerekiyor. Aynı zamanda arka plan grafiklerini bir karakter kaydırıp tazelemen gerekiyor.

Cycle say'dan kastım, bir loop ile bekleyebilirsin. Ya da burada her zaman aynı cycle tutan bir işlemi yapabilirsin. Ya da zaman yetiyorsa yeniden raster kesmesi programlayabilir ve bekleyebilirsin.  (vic'in 1 raster satırı kaç cycle tutuyor, border kaç cycle tutuyor bilmiyorum. Eğer çok büyükse bu süre, o zaman bayağı cycle harcamış olursun. Bu durumda en iyisi 3 kere raster interrupt kurmak daha mantıklı olur herhalde.)

Hızı yavaşlatmak için bir adet bitmask kullanabilirsin. yani diyelim ki #10101010 (170) bu senin mask'ın olsun. 8 irq'nun sadece 4'ünde kaydırma yapılacak. Ya da 00010001 olunca 2 kaydırma. 11111111 (255) olursa 8'inin 8'inde kaydırma var. Hızlandırmak için, bitmask 255'e geldikten sonra, x'i artırıp bitmask'ı yine düşüğe ayarlayarak hızı arttırırsın.

Burada hızı değiştirmek için iki değişken var, x ve bitmask. Zaten kaydırma rutini her ikisini de kontrol ettiği için hızı değiştirmek için bu değerleri modifiye etmen yeterli. Bitmap için bir tablo tutarsın hızlıdan yavaşa,
0-10000000
1-10001000
2-10010010
3-10101010
4-11010110
5-11011011
6-11101110
7-11111111

şeklinde bu tablodan çekersin 8 bitmask yavaşlama seviyesi, ve 8 (x) seviyesi 8x8 64 hız seviyesi eder. Bunları bile bir tabloya koyabilirsin. 64 girdili bir tablo yap, ordan direk yerleştirirsin.

Tabii şimdi ben bir spectrumcu olarak biraz işleri dolaylı yapıyor olabilirim. Lütfen c64'cüler atlasın konuya :D Yani görüldüğü üzere timer falan kullanmadım, sadece 1 raster irq kullanarak tüm kaydırmaları yaptım.

Bilmem anlatabildim mi :)


edit:Mantığı anlatmak için mecburen speccy'den örnek vericem.
Speccy'de 1 raster 228 ts tutuyor. Raster border'ı çizerken 80 ts var. Kaydırmayı border alanında yapmak akıllıca. d016'yı border çizilirken ayarlarsın, sonra 228x24 beklemek gerek (5400 cycle beklemek için sabit tstate tutan bir loop yapılabilir, bu sürede iş de yapılabilir.) Sanırım PAL c64'de 1 raster 63 cycle tutuyormuş (kaynak: http://unusedino.de/ec64/technical/misc/vic656x/pal-ntsc.html). Bu durumda 3 karakter satırı için, 63x24= 1512 cycle beklemek gerek. Eğer bu süre senin için değerliyse ozaman en iyisi irq programlayıp işe geri koyulmak gerek.

Haa tabii ben bütün bunları 8x8'lik karakter ekranı için söyledim, 8x4'lük ekran modunda, ona göre ayarlamak lazım.

Çevrimdışı hades

  • RAAT
  • Retro Meraklısı
  • *
  • İleti: 192
Ynt: Parallax mantığı
« Yanıtla #6 : 12 Aralık 2013, 19:34:24 »
.....
lda #$32
RASTER1 cmp $d012
bne RASTER1

jsr SCROLL1

lda #$74
RASTER2  cmp $d012
bne RASTER2

jsr SCROLL2

lda #$b2
RASTER3  cmp $d012
bne RASTER3

jsr SCROLL3
....
diğer rutinler
....
jsr $1003 ;müziksiz olmaz :)
jmp $ea81 ;veya jmp $ea31

;---------------------------
SCROLL1 .....
.....
rts

SCROLL2 ....
....
rts

SCROLL3 ....
.....
rts
;------------------
c64'te uzun zaman oldu intro kodlamayalı.

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Parallax mantığı
« Yanıtla #7 : 01 Ocak 2014, 23:22:34 »
Herkese mutlu yıllar. Kendi adıma 2014'ün ilk meyvası olarak, oldukça ilkel olmakla beraber, bir scroller yazmayı ve kazasız belasız çalıştırmayı başardım. Karakter tabanlı kaydırma yaptığım için gözlere biraz zararlı olsa da zamanlama olayını iyi kotardığımı düşünüyorum.

İlgileneler için .prg ve .asm dosyaları ekte

Yazdığım kodda hoşuma gitmeyen kısım aşağıda örneğini vermiş olduğum move_row alt rutininden 4 tane yazmış olmam. Satır numarasını parametre olarak gönderebilseydim daha mutlu olurdum. Ama bunun oluru yok herhalde.

Kod: [Seç]
!zone move_row1{
move_row1:
ldx #$00
ldy SCREEN_RAM+40*14
.loop
lda SCREEN_RAM+40*14+1,x
sta SCREEN_RAM+40*14,x
inx
cpx #40
bne .loop

sty SCREEN_RAM+40*14+39

rts
}


Bir diğer sorun da hız. Amacım bütün ekranı kaydırmaktı ama buna zaman kalmıyor malesef. IRQ'yu ekranın hemen altından başlatıyorum ($FB). Şu anda bile kaydırma işleri bitene kadar raster ekranın tepesine dayanıyor. Gerçi bununla ilgili bazı hinlikler düşünüyorum. Ama önce smooth scroll olayıyla ilgilenmem lazım.
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı Dejavu

  • Retroman
  • ***
  • İleti: 50
Ynt: Parallax mantığı
« Yanıtla #8 : 02 Ocak 2014, 00:40:37 »
Emeğine sağlık.
Bir video gibi birsey koyman mumkun mu?
Amiga1200/Apollo1260@80MHz/RapidRoadUSB/IndiAGAmkcr2
Amiga500+/ACA500/ACA1233n@55MHz/IndivisionECS
Turbo Chameleon 64 w/Docking Station
RaspberryPi Zero/1B/2B/3B
Lattepanda 4/64GB
ACA1221EC@42MHz

Çevrimdışı Ref

  • Yönetici
  • Özgür Retrocu
  • *
  • İleti: 3093
  • Advanced User Simulator
    • ae unutmadan
Ynt: Parallax mantığı
« Yanıtla #9 : 02 Ocak 2014, 01:20:55 »
Emeğine sağlık.
Bir video gibi birsey koyman mumkun mu?

vice c64 emülatör:
http://vice-emu.sourceforge.net/index.html#download

Dejavu, bu programı kur, .prg dosyasını sürükle bırak.

@wizofwor
Hayırlı olsun :D
Karakter tabanlı olması biraz göze rahatsız edici gelse de sanırım doğru yoldasın. Diğer taraftan arka planda olacaksa bu grafikler o kadar da önemli olmayabilir.

Yarın öğleden sonra biraz incelerim, kodu paylaşmayı düşünüyor musun?

Bu arada eğer 7dx'e katılacaksan, bu scroller oyun haline gelmeye başladığında forumlara koyma, skate sonra kabul etmiyor. Saçmalığın daniskası ama ille de gizli saklı kodlanacak bu release'ler :D

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Parallax mantığı
« Yanıtla #10 : 02 Ocak 2014, 09:09:23 »
Kodu paylaştım zaten. Biraz uzunca olduğu için mesajın içine gömmek istemedim ama ek olarak paylaştım.

Belli bir aşamayı geçtikten sonra en büyük sıkıntı neyi nasıl yapacağımı öğrenmek. Sanal ağda assembler ve VIC yazmaçlarını başlangıç seviyesinde öğrenmek için bol miktarda kaynak var. Sonrası el yordamıyla gidiyor. Örneğin akşam her şeyi hallettiğimi düşünürken efektin zamanlamasını ayarlayamadığımı fark ettim. Ne yaparsam yapayım sabit olarak 5sn.'de bir adım atlıyordu. IRQ rutimi kontrol ederken imdadıma Nightlord yetişti. Google'da 6510, IRQ, $d01a vs. ararken Nightnetwork denk geldi ve kodları karşılaştırınca CIA IRQ'ları kapatmadığımı fark ettim.

Kaynak oluşturması açısından bu tip basit kodları özellikle yayınlamak istiyorum. Hatta ilerde vaktim olursa İngilizce olarak da yayımlayabilirim. Tabi bunlar birebir oyun içinde kullanılacak kodlar değil. Kullansam bile basit işleri halleden alt rutinler olduğu için sorun çıkarmayacağını düşündüm. Kabul etmezlerse de canları sağolsun. Şu parallax'ı istediğim seviyeye getireyim sıra sprite muliplexer'a gelecek. Kurguladığım oyunda multiplexer'e gerek yok ama acayip heveslendim sıralama algoritmalarını vs. okumaya başladım bile.
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı Ref

  • Yönetici
  • Özgür Retrocu
  • *
  • İleti: 3093
  • Advanced User Simulator
    • ae unutmadan
Ynt: Parallax mantığı
« Yanıtla #11 : 02 Ocak 2014, 11:29:47 »
Kodu paylaştım zaten. Biraz uzunca olduğu için mesajın içine gömmek istemedim ama ek olarak paylaştım.

...

Kaynak oluşturması açısından bu tip basit kodları özellikle yayınlamak istiyorum. Hatta ilerde vaktim olursa İngilizce olarak da yayımlayabilirim. Tabi bunlar birebir oyun içinde kullanılacak kodlar değil.

Valla öğrenme kaynakları çok gibi görünüyor ama kaynağın "kendine uyanını" bulmak zor. Bu sebepten yayınladığın kaynak kodu bence çok değerli. Şimdi dalıyorum içine bakalım neymiş 6502 :)

Çevrimdışı witchdoktor

  • RAAT
  • Normalleşmiş Retroman
  • *
  • İleti: 757
Ynt: Parallax mantığı
« Yanıtla #12 : 02 Ocak 2014, 14:55:55 »
@wizofwor

Güzel bir girişim olmuş, ellerine sağlık.

Ekranın genelde 25 satırını da kaydırmak çoğunlukla gerekmez, skor paneli vb alanlar nedeniyle. Bunun dışında, doublebuffering ile 2. bir ekranda her frame'de ekranın belli bir kısmını (örneğin ekran kayma hızı 1 pixel/frame ise, her frame'de ekranın 1/8'lik kısmını) kopyalayarak CPU gücünden tasarruf edebilirsin.

Sprite multiplexer işi de güzel bir 'challenge' olur bu arada.

Hayırlı kodlamalar...

Çevrimdışı wizofwor

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 398
Ynt: Parallax mantığı
« Yanıtla #13 : 02 Ocak 2014, 15:21:28 »
Doublebuffering tekniğini okudum. Ama uygulaması nasıl yapılır hiç kafa yormadım. 4 satır farklı hızla kayarken biraz kafa karıştırıcı olur gibi geldi. Hız sorununun üstesinden gelmek için farklı sayaç değerlerinde farklı satırları kaydırmayı düşünüyorum. Ama önce smooth scrolling olayını halletmem lazım.

@Dejavu: Video olayı için hazırda pratik bir yöntemim yok ama bir yolunu bulur video'da eklerim.
Gosub ile gidilen yerden goto ile dönen adam

Çevrimdışı nightlord

  • RAAT
  • Tedavideki Retromanik
  • *
  • İleti: 389
    • Night Network
Ynt: Parallax mantığı
« Yanıtla #14 : 03 Ocak 2014, 00:42:17 »
Ellerine sağlık wizofwar. Ben bu thread'i de önceden kaçırmışım. Birkaç naçizane notum olacak

Alıntı yapılan: wizofwor
Yazdığım kodda hoşuma gitmeyen kısım aşağıda örneğini vermiş olduğum move_row alt rutininden 4 tane yazmış olmam. Satır numarasını parametre olarak gönderebilseydim daha mutlu olurdum. Ama bunun oluru yok herhalde.

Kod: [Seç]
!zone move_row1{
move_row1:
ldx #$00
ldy SCREEN_RAM+40*14
.loop
lda SCREEN_RAM+40*14+1,x
sta SCREEN_RAM+40*14,x
inx
cpx #40
bne .loop

sty SCREEN_RAM+40*14+39

rts
}


Bu soru iki türlü algılanabilir:
1- kodu 4 kere yazmaktan rahatsızsın ama bellekte 4 kere yer kaplaması OK
2- kodun bellekte de 4 kat yer kaplamasından rahatsızsın

Birincisinin basit bir çözümü var: makro kullanmak
Kod: [Seç]
!macro moveRow .row{
ldx #$00
ldy SCREEN_RAM+40*.row
.loop
lda SCREEN_RAM+40*.row+1,x
sta SCREEN_RAM+40*.row,x
inx
cpx #40
bne .loop

sty SCREEN_RAM+40*.row+39

rts
}

; sonra ileride
moveRow1:
    +moveRow 14

moveRow2:
    +moveRow 15

moveRow3:
    +moveRow 16

moveRow4:
    +moveRow 17


Hatta ayni macroyu MoveLogo rutininde de kullanabilirsin. Bellekteki açılım şu anki haliyle aynı ama en azından kopyala yapıştır hatalarından seni korur.

Eğer bir rutinim olsun bellekte diyorsan, o zaman daha esnek bir rutin yazmak lazım. Burada problem verilen parametrenin (.row) çarpma işlemine girecek olması. o çarpmayı da tek rutin olduğu zaman Acme'ye yaptırmak mümkün değil. Bu yüzden çarpmayı compile time değil runtime da yapmak zorundasın.

Her C64 coder'ı "çarpma", "bölme" gibi kelimeler duyduğunda bir durup, bundan nasıl kurtulurum diye bakmalı. Burada da hemen row'un kaç değişik değer alabileceğine bakıyoruz. 25. hemen 25 değerlik bir tablo yapıyoruz. Daha doğrusu alt baytlar ve üst baytlar için iki tablo yapıyoruz:

Kod: [Seç]
scr_rows_lo:
!by <(SCREEN_RAM + 40 * 0)
!by <(SCREEN_RAM + 40 * 1)
...
!by <(SCREEN_RAM + 40 * 23)
!by <(SCREEN_RAM + 40 * 24)


scr_rows_hi:
!by >(SCREEN_RAM + 40 * 0)
!by >(SCREEN_RAM + 40 * 1)
...
!by >(SCREEN_RAM + 40 * 23)
!by >(SCREEN_RAM + 40 * 24)
...

;row is passed in X
moveRow:
!zn{
    lda scr_rows_lo, x
    sta .r + 1
    clc
    adc #1
    sta .w + 1

    lda scr_rows_hi, x
    sta .r + 2
    sta .w + 2

    ldx #$00
.r:
    lda $1234,x
.w:
    sta $1234,x
    inx
    cpx #40
    bne .r
    rts
}


Tabii butun bunları yaptığında başlangıçtan daha da çok bellek harcamış olabilirsin :) Bu yüzden böyle bir tasarım kararının bir diğer ayağı da bu rutinin kaç değişik yerden çağırılıyor olduğu. n yerden çağırılan ve basit hali x bayt harcayan bir rutinin daha esnek ve karmaşık hali y bayt harcıyorsa (y > x), o zaman nx ile y'yi karşılaştırıp ona göre karar vermek lazım. n yeterince büyükse bellek kazanacaksın demektir.

Bir de en son moveLogo rutininle ilgili bir yorumum var. orada 5 ayrı loop kullanmak yerine hepsini tek loopta halledebilirsin. Bunun için birinci harfi 39. lokasyona koymak icin y registerini kullanmaktan vazgeçmen gerekir. Ben olsam önce 5 satirin birinci lokasyonlarındaki harfi alır bir yerlere yazardım (zero page olabilir). sonra bir loopta ard arda 5 tane "lda foo+1,x sta foo, x" yapar ardindan da en son o 5 sakladigim karakteri 39. pozisyonlara koyardim.