위에 올려놓은 영상처럼 알만툴로 탄막게임을 만드는 방법에 대한 글이에요, 여기서 사용한 알만툴은 rpg maker vx ace에요


알만툴의 화면에 그림을 띄워놓을수 있는 기능을 사용해서 탄막슈팅 게임을 만든거에요





[이미지 회전 링크]


그리고 이 글에서 나오는 코드를 사용하려면 위에 올려놓은 링크에 있는 방법대로 알만툴 내에 이미지를 특정 각도만큼 회전시키는 명령어를 추가해야해요




1. 배열의 구조


[ob, image, size, angle, x, y, vx, vy, timer, hp, mode]


기본적으로 탄막게임내의 샷, 에너미, 탄은 모두 오브젝트로 정의되요, 그리고 위에 올려놓은 배열이 오브젝트를 정의하는 배열이에요


ob는 오브젝트의 종류를 나타내요, 1이면 플레이어가 발사하는 샷이고, 2면 에너미고, 3이면 에너미가 발사하는 탄이에요


imgae는 해당 오브젝트를 표시하는대 사용하는 이미지파일의 이름이에요


size는 해당 오브젝트의 충돌체크에서 사용하는 오브젝트의 사이즈에요


angle은 오브젝트 이미지의 회전각도에요, 실제 진행방향이 아니라 이미지만 회전시키는 각도에요


x, y 는 오브젝트의 현재 xy좌표에요


vx vy는 오브젝트의 x축속도 y축속도에요, 이 값을 바탕으로 오브젝트가 움직여요


timer는 오브젝트가 생성되고 나서 지나간 시간이에요, 매 프레임마다 1씩 늘어나요


hp는 오브젝트의 체력이에요, 0이되면 삭제되요


mode는 오브젝트가 특수한 움직임을 할때 그걸 정의하는 부분이에요, 에너미가 움직이고 탄을 발사하도록 하는대에 사용되요


즉 플레이어가 샷을 발사하거나, 에너미를 생성하거나, 에너미가 탄을 발사하는것같이 오브젝트를 생성하려면 $object의 배열에 위의 형식대로 요소를 추가하면 되요, 마찬가지로 오브젝트를 제거할때에도 $object의 배열에서 해당 요소를 제거하면 되요






2. 게임의 코드



#ㅡㅡㅡㅡㅡ초기설정ㅡㅡㅡㅡㅡ

def stage_start()

☆이 함수는 게임을 시작하기 전에 한번만 실행해서 초기값을 정의하는 함수에요


#inside 함수정의

def inside(x,y,r,l,t,b)

if x > r

return 0

end

if x < l

return 0

end

if y > t

return 0

end

if y < b

return 0

end

return 1

end

☆xy가 r l t b 범위 내에 있는지 없는지를 출력해주는 함수에요


#insdie_cut 함수정의

def inside_cut(x,y,r,l,t,b)

x1, y1 = x, y

if x>r

x1 = r

end

if x<l

x1 = l

end

if y<t

y1 = t

end

if y>b

y1 = b

end

return x1,y1

end

☆ xy를 r l t b 범위 내의 값으로 제한시켜서 출력해주는 함수에요


#input_arrow 함수정의

def input_arrow()

angle = nil

if ( Input.press?(:DOWN) && Input.press?(:RIGHT) ) && angle == nil

angle = (Math::PI/180)*45

end

if ( Input.press?(:DOWN) && Input.press?(:LEFT) ) && angle == nil

angle = (Math::PI/180)*135

end

if ( Input.press?(:UP) && Input.press?(:LEFT) ) && angle == nil

angle = (Math::PI/180)*225

end

if ( Input.press?(:UP) && Input.press?(:RIGHT) ) && angle == nil

angle = (Math::PI/180)*315

end

if Input.press?(:RIGHT) && angle == nil

angle = 0

end

if Input.press?(:DOWN) && angle == nil

angle = (Math::PI/180)*90

end

if Input.press?(:LEFT) && angle == nil

angle = (Math::PI/180)*180

end

if Input.press?(:UP) && angle == nil

angle = (Math::PI/180)*270

end

return angle

end

☆알만툴의 기본 스크립트를 통해서 플레이어가 방향키(8방향)를 입력하면 해당 방향을 각도로 출력해주는 함수에요


#변수&배열 초기값설정

$player_x = 272

$player_y = 316


$mode = 0

$speed = 4


$life = 5

$shot_cool = 0

$hit_cool = 0


$object = Array.new


$image_number = 0


$stage_timer = 0

$stage_level = 0

☆ 게임에 쓰이는 여러 값들의 초기값을 설정하는 부분이에요


#배경이미지 표시

screen.pictures[1].show("back1", 0, 0, 0, 100, 100, 255, 0)

☆ 배경이미지를 표시하는 부분이에요,


end

#ㅡㅡㅡㅡㅡ초기설정끝ㅡㅡㅡㅡㅡ





#ㅡㅡㅡㅡㅡ반복ㅡㅡㅡㅡㅡ

def stage_repeat()

☆이 함수가 실제로 1프레임마다 반복되면서 게임을 진행시키는 함수에요


#A 스테이지작동


if $stage_level == 0

if $stage_timer <= 120

$stage_timer += 1

end

if $stage_timer == 120

$object.push([2, "boss1", 48, 0, 272, 0, 0, 0, 0, 60, 1])

end

end


if $stage_level == 1

if $stage_timer <= 240

$stage_timer += 1

end

if $stage_timer == 240

$object.push([2, "boss2", 48, 0, 272, 0, 0, 0, 0, 60, 2])

end

end


if $stage_level == 2

if $stage_timer <= 360

$stage_timer += 1

end

if $stage_timer == 360

$object.push([2, "boss3", 48, 0, 272, 0, 0, 0, 0, 60, 3])

end

end


if $stage_level == 3

if $stage_timer <= 480

$stage_timer += 1

end

if $stage_timer == 480

$object.push([2, "boss4", 48, 0, 272, 0, 0, 0, 0, 60, 4])

end

end


if $stage_level == 4

if $stage_timer <= 600

$stage_timer += 1

end

if $stage_timer == 600

SceneManager.call(Scene_Title)

end

end

☆ 스테이지의 진행을 처리하는 부분이에요, 순서대로 4명의 보스가 나오고 다 잡으면 게임을 종료해요


#B 플레이어 조작


#B1 플레이어 shift키 고속&저속 변경

if Input.press?(:A)

$mode = 1

$speed = 2

else

$mode = 0

$speed = 4

end

☆ shift키를 인식해서 고속모드와 저속모드를 변경하는 부분이에요


#B2 플레이어 방향키 이동

if input_arrow() != nil

$player_x += $speed*Math.cos(input_arrow())

$player_y += $speed*Math.sin(input_arrow())

$player_x, $player_y = inside_cut($player_x, $player_y, 544-24, 0+24, 0+24, 416-24)

end

☆방향키를 입력한 방향대로 플레이어 기체를 움직이는 부분이에요


#B3 플레이어 z키 샷발사

if Input.press?(:C) && $shot_cool <= 0

$object.push([1, "shot1", 10, 270*Math::PI/180, $player_x, $player_y, 0, -8, 0, 99, 0])

RPG::SE.new("Cursor1", 80, 100).play

$shot_cool = 12

end

☆z키를 눌러서 플레이어가 샷을 발사하는 부분이에요


#B4 샷발사 & 피탄 쿨타임

if $shot_cool > 0

$shot_cool -= 1

end

if $hit_cool > 0

$hit_cool -= 1

end

☆샷을 발사하거나 피탄되었을때 쿨타임을 처리하는 부분이에요


#C 오브젝트 작동


#C1 오브젝트 기본작동 - vx vy timer

i = 0

while $object[i] != nil

$object[i][4] += $object[i][6]

$object[i][5] += $object[i][7]

$object[i][8] += 1

i+=1

end

☆오브젝트(플레이어가 발사한 샷, 에너미, 에너미가 발사한 탄)를 x축속도와 y축속도대로 움직이고 타이머를 증가시키는 부분이에요


#C2 오브젝트 특수작동 - mode

i = 0

while $object[i] != nil


#ㅡㅡㅡㅡㅡmode1ㅡㅡㅡㅡㅡ

if $object[i][ 10 ] == 1


if $object[i][8] >= 0 && $object[i][8] < 60

$object[i][5] += 2

end


if $object[i][8] >= 60 && $object[i][8] % 20 == 0

RPG::SE.new("Evasion1", 80, 100).play

random = rand(20)*Math::PI/180

for k in 0..18 do

angle = random + k*20*Math::PI/180

$object.push([3, "bullet1", 10, angle, $object[i][4], $object[i][5], 3*Math.cos(angle), 3*Math.sin(angle), 0, 99, 0])

end

end


if $object[i][8] >= 60 && $object[i][8] % 60 == 0

RPG::SE.new("Evasion1", 80, 100).play

aim = Math.atan2($player_y-$object[i][5],$player_x-$object[i][4])

for k in 0..15 do

angle = aim + k*24*Math::PI/180

$object.push([3, "bullet3", 10, angle, $object[i][4], $object[i][5], 2*Math.cos(angle), 2*Math.sin(angle), 0, 99, 0])

end

end



if $object[i][9] <= 0

$stage_level += 1

end


end

#ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ



#ㅡㅡㅡㅡㅡmode2ㅡㅡㅡㅡㅡ

if $object[i][ 10 ] == 2


if $object[i][8] >= 0 && $object[i][8] < 60

$object[i][5] += 2

end


if $object[i][8] >= 60 && $object[i][8] % 30 == 0

RPG::SE.new("Evasion1", 80, 100).play

random = rand(30)*Math::PI/180

for k in 0..12 do

angle = random + k*30*Math::PI/180

$object.push([3, "sword6", 10, angle+30*Math::PI/180, $object[i][4]+45*Math.cos(angle), $object[i][5]+45*Math.sin(angle), 3*Math.cos(angle+30*Math::PI/180), 3*Math.sin(angle+30*Math::PI/180), 0, 99, 0])

end

end


if $object[i][8] >= 60 && ($object[i][8]+15) % 30 == 0

RPG::SE.new("Evasion1", 80, 100).play

random = rand(30)*Math::PI/180

for k in 0..12 do

angle = random + k*30*Math::PI/180

$object.push([3, "sword6", 10, angle+-30*Math::PI/180, $object[i][4]+45*Math.cos(angle), $object[i][5]+45*Math.sin(angle), 3*Math.cos(angle+-30*Math::PI/180), 3*Math.sin(angle-30*Math::PI/180), 0, 99, 0])

end

end


if $object[i][8] >= 60 && $object[i][8] % 60 == 0

RPG::SE.new("Evasion1", 80, 100).play

aim = Math.atan2($player_y-$object[i][5],$player_x-$object[i][4])

for k in 0..15 do

angle = aim + k*24*Math::PI/180

$object.push([3, "sword7", 10, angle, $object[i][4], $object[i][5], 2*Math.cos(angle), 2*Math.sin(angle), 0, 99, 0])

end

end


if $object[i][9] <= 0

$stage_level += 1

end


end

#ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ



#ㅡㅡㅡㅡㅡmode3ㅡㅡㅡㅡㅡ

if $object[i][ 10 ] == 3


if $object[i][8] >= 0 && $object[i][8] < 60

$object[i][5] += 2

end


if $object[i][8] >= 60 && $object[i][8] % 60 == 0

RPG::SE.new("Evasion1", 80, 100).play

random = rand(20)*Math::PI/180

for k in 0..18 do

angle = random + k*20*Math::PI/180

$object.push([3, "music3", 8, 0, $object[i][4]-150, $object[i][5], 3*Math.cos(angle), 3*Math.sin(angle), 0, 99, 0])

end

end


if $object[i][8] >= 60 && ($object[i][8]+15) % 60 == 0

RPG::SE.new("Evasion1", 80, 100).play

aim = Math.atan2($player_y-$object[i][5],$player_x-$object[i][4])

for k in 0..12 do

angle = aim + k*30*Math::PI/180

$object.push([3, "music5", 8, 0, $object[i][4], $object[i][5], 2*Math.cos(angle), 2*Math.sin(angle), 0, 99, 0])

end

end


if $object[i][8] >= 60 && ($object[i][8]+30) % 60 == 0

RPG::SE.new("Evasion1", 80, 100).play

random = rand(20)*Math::PI/180

for k in 0..18 do

angle = random + k*20*Math::PI/180

$object.push([3, "music3", 8, 0, $object[i][4]+150, $object[i][5], 3*Math.cos(angle), 3*Math.sin(angle), 0, 99, 0])

end

end


if $object[i][8] >= 60 && ($object[i][8]+45) % 60 == 0

RPG::SE.new("Evasion1", 80, 100).play

aim = Math.atan2($player_y-$object[i][5],$player_x-$object[i][4])

for k in 0..12 do

angle = aim + k*30*Math::PI/180

$object.push([3, "music5", 8, 0, $object[i][4], $object[i][5], 2*Math.cos(angle), 2*Math.sin(angle), 0, 99, 0])

end

end


if $object[i][9] <= 0

$stage_level += 1

end


end

#ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ


#ㅡㅡㅡㅡㅡmode4ㅡㅡㅡㅡㅡ

if $object[i][ 10 ] == 4


if $object[i][8] >= 0 && $object[i][8] < 60

$object[i][5] += 2

end


if $object[i][8] >= 60 && ( $object[i][8] % 5 == 0 && $object[i][8]%120 >= 60 )

RPG::SE.new("Evasion1", 80, 100).play

aim = Math.atan2($player_y-$object[i][5],$player_x-$object[i][4])

for k in -5..5 do

angle = aim + k*20*Math::PI/180

$object.push([3, "knife9", 5, angle, $object[i][4], $object[i][5], 3*Math.cos(angle), 3*Math.sin(angle), 0, 99, 0])

end

end



if $object[i][8] >= 60 && $object[i][8] % 20 == 0

RPG::SE.new("Evasion1", 80, 100).play

x = rand(544)

angle = Math.atan2($player_y-0,$player_x-x)

$object.push([3, "bullet8", 10, angle, x, 0, 2*Math.cos(angle), 2*Math.sin(angle), 0, 99, 0])

end


if $object[i][9] <= 0

$stage_level += 1

end


end

#ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ


i+=1

end

☆첫번째~네번째 에너미들을 움직이는 부분이에요, 에너미가 움직이고 탄을 발사하는 작동들이 여기서 처리되요


#C3 오브젝트 화면밖&hp0 제거

i = 0

while $object[i] != nil

if inside($object[i][4], $object[i][5], 544+50, 0-50, 416+50, 0-50) == 0 || $object[i][9] <= 0

$object.delete_at(i)

else

i+=1

end

end

☆오브젝트가 화면 밖으로 벗어났거나 hp가 0이되었을때 제거하는 부분이에요


#D 충돌처리


#D1 플레이어 - 에너미&탄 충돌처리

i = 0

while $object[i] != nil

if $object[i][0] == 2 || $object[i][0] == 3

if inside($object[i][4], $object[i][5], $player_x+20, $player_x-20, $player_y+20, $player_y-20) == 1

if ( $player_x-$object[i][4] )**2+( $player_y-$object[i][5] )**2 < $object[i][2]**2 && $hit_cool == 0

RPG::SE.new("Blow1", 80, 100).play

$life -= 1

$hit_cool = 120

end

end

end

i+=1

end

☆플레이어와 에너미, 플레이어와 탄의 충돌을 체크하는 부분이에요,


#D2 에너미-샷 충돌처리

i = 0

while $object[i] != nil

if $object[i][0] == 2


k = 0

while $object[k] != nil

if $object[k][0] == 1

if ($object[i][4]-$object[k][4])**2+($object[i][5]-$object[k][5])**2 < $object[i][2]**2

RPG::SE.new("Knock", 80, 100).play

$object[i][9] -= 1

$object[k][9] = 0

end

end

k +=1

end


end

i+=1

end

☆에너미와 플레이어의 샷의 충돌을 체크하는 부분이에요



#E 이미지 랜더링


#E1 잔기이미지

$image_rend = 2

screen.pictures[2].show("life"+$life.to_s, 0, 544-80, 416-16, 100, 100, 255, 0)


#E2 샷 이미지

i=0

while $object[i] != nil

if $object[i][0] == 1

$image_rend += 1

screen.pictures[$image_rend].show($object[i][1], 1, $object[i][4], $object[i][5], 100, 100, 255, 0)

screen.pictures[$image_rend].turn($object[i][3])

end

i+=1

end


#E3 플레이어 이미지

$image_rend += 1

if $hit_cool == 0

a = 255

else

a = 100

end

if $mode == 0

screen.pictures[$image_rend].show("player1", 1, $player_x, $player_y, 100, 100, a, 0)

else

screen.pictures[$image_rend].show("player2", 1, $player_x, $player_y, 100, 100, a, 0)

end



#E4 에너미 이미지

i=0

while $object[i] != nil

if $object[i][0] == 2

$image_rend += 1

screen.pictures[$image_rend].show($object[i][1], 1, $object[i][4], $object[i][5], 100, 100, 255, 0)

screen.pictures[$image_rend].turn($object[i][3])

end

i+=1

end


#E5 탄 이미지

i=0

while $object[i] != nil

if $object[i][0] == 3

$image_rend += 1

screen.pictures[$image_rend].show($object[i][1], 1, $object[i][4], $object[i][5], 100, 100, 255, 0)

screen.pictures[$image_rend].turn($object[i][3])

end

i+=1

end

☆이미지를 랜더링하는 부분이에요, 순서대로 잔기-샷-플레이어-에너미-탄 순서대로 랜더링해요, 즉 탄의 이미지가 제일 위에 놓이게 되요


#E6 초과이미지 제거

if $image_number > $image_rend

for i in $image_rend+1..$image_number do

screen.pictures[i].erase

end

end


$image_number = $image_rend

☆이미지를 모두 생성하고 난 뒤에 만약 이전프레임보다 적은 갯수의 이미지를 생성해서 이전프레임에서 생성한 이미지들이 남아있다면 그렇게 남아있는 이미지를 제거하는 부분이에요, 현재 프레임에서 이미지를 생성하면서 갯수를 체크한다음, 그 값을 이전 프레임의 이미지의 갯수와 비교해서 만약 남아있는 이미지가 있다면 제거하고, 현재 프레임의 이미지 갯수를 저장해요, 이 값은 다음 프레임에서 비교용으로 쓰이게 되요


#F 게임오버

if $life == 0

SceneManager.call(Scene_Gameover)

end

☆게임오버를 처리하는 부분이에요


end

#ㅡㅡㅡㅡㅡ반복끝ㅡㅡㅡㅡㅡ



위에 올려놓은 코드가 실제로 전체 게임의 코드에요, 





28ec9e36ebd518986abce8954587726cdc82




그리고 위에 올려놓은 사진처럼 알만툴 내의 스크립트 입력창에 위의 코드를 입력하면 되요, 파랑색 ☆로 표시된 설명부분은 제외하고 입력하면 되요




28ef9e36ebd518986abce8954489766a09a8



그런다음 위의 사진처럼 이벤트를 하나 생성해서 앞의 코드로 정의한 두개의 함수를 실행시키면 되요, 초기값을 정의하는 함수는 한번만 실행시키고, 그 다음 게임을 진행하는 함수를 루프기능으로 1프레임마다 반복시키면 되요


이렇게하면 맨 처음의 영상처럼 알만툴로 탄막슈팅게임을 만들수 있어요






2fbcd32db49c28a8699fe8b115ef046fef19d1eccc


2fb2c335b49c28a8699fe8b115ef046c250c25


2fb2c335b79c28a8699fe8b115ef046cf75137


2fb2c335b69c28a8699fe8b115ef04647791


2fb2c335b19c28a8699fe8b115ef0464338c


2fa8dc2ae0c669e87eb1d19528d5270315c32c15


2fa8dc2ae0c66be87eb1d19528d52703b69fb16f


2fa8dc2ae0c660e87eb1d19528d527037779d0c4


26b3d920e08b76b660b8f68b12d21a1da12941


21b4d623b59c28a8699fe8b115ef046ff9d8


21b4d623b49c28a8699fe8b115ef046fbb3b


21b4d623b79c28a8699fe8b115ef046f6c1e


21b4d623b69c28a8699fe8b115ef046fae71


21b4d623b19c28a8699fe8b115ef046fddd7


21b4d623b09c28a8699fe8b115ef046f1e58


20a8c32fe68176b660b8f68b12d21a1d6b9736


20a8c32fe68776b660b8f68b12d21a1dd2b929


3db1d13fe0c069e87eb1d19528d52703b12fa5c7


3db1d13fe0c06ae87eb1d19528d52703c9fd9187


3eb5df32b49c28a8699fe8b115ef046f30ad


3eaadf34e18476b660b8f68b12d21a1df61c2f


3eaadf34e18576b660b8f68b12d21a1d2f3709


39b4c42ae09c28a8699fe8b115ef0468e6bb0e






그리고 위에 올려놓은 이미지들이 게임에서 사용되는 이미지 파일들이에요, 알만툴의 리소스에서 Picture 부분에 넣어놓으면 되요