Tutorial
Page 3 of 10
Creating a Surface
튜토리얼용 게임을 해봤다면 플레이어와 적, 모든 오브젝트에 그림자가 있는 것을 봤을겁니다.
하지만 벽에는 아무 것도 없습니다. 이건 상당히 어색하고 밋밋해보입니다. 지금부터 벽 오브젝트의 draw event에서 이걸 고칩니다 :
If you play the game that we have created for this tutorial,
you can see that the player, the enemies and all other objects in the game have shadows.
However, the walls do not and that makes the game look odd, and "flat". Now, to fix that we can do this in the draw event of the wall object:
이렇게 보입니다:
Which looks like this:
하지만 좋아보이지도 않고 사실감도 없습니다. 우리는 벽에 적은 반투명 효과상태로 여러번 반복해서 단계적으로 밝아지도록 그래서 더 낫게 보이게끔 할 수 있습니다. 다음 코드를 써봅니다:
But this looks quite bad and is not at all realistic. We could make it better by repeating the drawing of the wall but with a low alpha so that it gradually fades and also has a feeling of depth by using this code:
결과는 이렇게 보입니다:
Which will look like this:
확실히 개선되었습니다! 하지만 두가지 문제점이 있습니다..
(1)
그림자의 클리핑 처리가 벽 오브젝트에 너무 의존적이다.
첫번째 분명한 점은 그림자가 벽 오브젝에 의해 클리핑 된 점입니다. 이건 벽 오브젝트가 모두 같은 깊이를 가진 상태로 그려졌기 때문에 가능합니다. 게임 메이커는 이 벽이 항상 같은 깊이 값을 갖는다는 보장을 해주지 않습니다. (값이 바뀔 수도 있다는 뜻이지요) 하지만 그래도 우리는 이 깊이 값을 바꾸고 싶지 않을겁니다. 바꾸게 되면 골치 아파지니까요.
(2)
많은 그리기를 하는 그림자를 쓰는 인스턴스가 너무 많습니다. 그림자를 필요로 하는 벽이 20개 정도 되는데 총 320번의 그리기를 하고 있습니다. 이건 엄청난 낭비입니다. 간단한 게임인데도 디바이스에 버벅임을 가져올 수 있습니다.
Now THAT is an improvement! But there are still two problems here...
the first and most obvious one is that the shadows are being "clipped" by other wall objects. This is due to the fact that the instances of the wall object are all at the same depth and so GameMaker cannot guarantee that they will be drawn in a specific order at any time, and we don't want to change their depth either, as that is just too complicated.
Which leads to the second problem... Having so many instances running a loop to draw like that means that if we have 20 wall instances, we are actually drawing them 320+ times! That is extremely
inefficient and makes a simple game like this more likely to lag on devices.
이제 어떻게 할 것인가? 이젠 서피스를 사용해서 벽의 그림자를 만들 겁니다.
그림자를 만들기 위해서 사용하는 첫 초기화 그리기를 끝내고 나서부터는 서피스 그리기 한번만 사용하므로 훨씬 효과적입니다.
So, what are we going to do? We are going to use a surface to create and draw our wall shadows. this is much more efficient as, after the initial draw calls to create the shadows, it requires only one
draw call to draw the surface each step.
시작해봅시다. 새 오브젝트를 만듧니다. 이름을 "obj_Shadow_Surface"로 짓습니다. 깊이값은 -900을 줍니다. (벽 아래에 그려지도록) 이 오브젝트에 Create Event를 추가하고 다음 코드를 넣습니다:
Lets start then by creating a new object and calling it "obj_Shadow_Surface". Give it a depth of -900 (so it is drawn under the wall instances), then give it a create event and add this code:
저 변수는 우리의 서피스를 가리킵니다. 여기에서 만들 수도 있지만 이미 서피스가 있는지 없는지 확인해야 합니다. (서피스는 상태가 불확실합니다.) 이걸 확인한 후에 그림자를 그립니다. 그러나 그 전에 방을 종료할때 서피스를 삭제하기 위해서 Room End Event를 추가합니다. 서피스가 필요 없어졌는데도 메모리를 삭제하지 않으면 메모리 누수가 발생되고 결국 게임이 다운될 수 있습니다. Room End Event에 다음 코드를 넣습니다:
That variable will hold our surface. We could create it here if we wanted to, but since we need to have a check to see if the surface exists or not (remember, surfaces are volatile!) we shall take advantage of the check itself to draw the initial shadows. But before that part, we also need to add a Room End Event to clean up at the end of the room. Since surfaces take up memory, if we do not free that memory when the surface is no longer needed, we end up with a memory leak which will eventually crash your game. So in the Room End Event add this code:
이제는 그림자 오브젝트에 그리기 이벤트를 넣습니다. 그리고 다음 코드를 추가합니다:
Now we can add the important draw event to our shadow object, and in it place this code:
위의 코드로 벽에 자연스러운 그림자가 생겼습니다, 이렇게.
With the above code your walls now have a beautiful gradient shadow that looks like this:
이 코드는 무슨 일을 했을까요? 봅시다:
- 서피스가 이미 있는지 확인합니다.
- 없으면 방의 크기만한 사이즈로 만들어서 "surf"에 할당합니다.
- 서피스의 그리는 대상 drawing target을 이 'surf' 서피스로 변경합니다.
- 서피스를 검은색으로 지웁니다.
- 서비스에 벽을 검게 그립니다. 위치를 조금씩 바꿔가면서 살짝 반투명하게 그립니다.
- 드로잉 타켓(그려지는 대상)을 게임 화면으로 다시 되돌립니다.
서피스가 생긴 순간부터, 화면 위에는 위의 코드 보다 서피스가 그려집니다.
From that moment on, the surface is then drawn to the screen every step rather than the above code.
But what does that code do? Let's see then:
- it checks for the surface
- if it doesn't exist it creates a surface the size of the room and stores it index in the variable "surf"
- the surface is then cleared to black, with 0 alpha
- it sets the drawing target to the surface
- it draws the walls, in black, at different positions and with a low alpha onto the surface
- it resets the drawing target (to draw to the screen)
From that moment on, the surface is then drawn to the screen every step rather than the above code.
서피스를 만들고 나서 바로 클리어 했습니다. 그건, 서피스는 비디오 메모리의 일부분이기 때문에 뭔가 그려졌던 흔적, 일종의 "노이즈" 가 남아 있을 수 있습니다. 따라서 만들자마자 깨끗하게 지우는 것은 중요한 일입니다. 화면에 뭔가 남아 있을지 모르니까요.
룸에 이 새 오브젝트를 추가하고 어떻게 보이는지 확인합니다.
Note that the surface is cleared when it is first created, as, since the surface is simply an area of vram that has been set aside, the surface can contain "noise" which may show up when you first use it. So clearing
it in this way "primes" the surface and makes sure that you have no unwanted artifacts when you start to use it normally. Add the new object into the room and give it a test to see how it all looks.
아직 설명하지 않은 부분이 있습니다. 서피스에 그릴때 extended blend mode를 썻습니다.
이게 무엇이냐면? 한번 삭제하고 어떻게 되는지 보세요.
기본적으로, 반투명(알파)하게 서피스에 작업할때 같은 부분에 반투명하게 여러번 그리는 것은 과용입니다.
겹쳐진 것처럼 보이기 위해서 각각 0.5씩의 반투명값으로 합하는 두장의 이미지를 예상했을지도 모릅니다. 0.75, 실제 최종 이미지는 이정도의 반투명한 상태로 완성됩니다.(정확한 뜻인가?) 이건 버그가 아니고 정상입니다. 하지만 반투명 값으로 여러번 그리는 것 또한 over-write 대신 단계적인 반투명 그리기를 하려면 special extended blend mode 를 써야합니다.
There is one further thing there that I haven't mentioned... The extended blend mode that we have used for drawing to the surface.
What is that for? Well, remove it and see what happens!
Basically, the way surfaces work with alpha is that when you draw twice to an area with different alpha values, these over-write each other.
So where you may expect two images with alphas of 0.5 to give a final image with an alpha of approx.
0.75, you will get the final surface having an alpha of 0.5 8the last alpha value drawn to it.
This is not a bug and is quite normal, but it means that to draw multiple alpha values and have the accumulate instead of over-write, we have to use that special extended blend mode.
extended blend mode는 그림자처럼 겹쳐지는 흑백 수준의 이미지에 대해서는 최선입니다.
하지만 색상이 있는 스프라이트에 대해서는 복잡한 픽셀 단위의 색상 연산을 필요하게 됩니다. 그래서 보통 이런 경우는 평소처럼 그립니다. (다음 페이지에서 보게 됩니다.)
서피스 작업을 할때는 이점을 명심합시다.
It's worth noting that this extended blend mode is ideal for overlaying shadows and other grey-scale images, but when dealing with coloured sprites it can be a lot more complicated to pre-multiply the coloured component of each pixel and so, in general, you will just draw as normal (as you will see on the following page).
Keep this in mind whenever you work with surfaces!!!
Next 버튼을 눌러서 다음 페이지로 갑시다.