4
\$\begingroup\$

I created a 2D tile-based jump and run game for Windows Phone and I want to draw a shadow under the character. How can I do that? The shadow must also be drawn on the tiles if the character jumps. The character and the tiles are rectangles.

How can I draw a shadow under my character?

The shadow should look similar to the shadows in this picture:

Shadow

UPDATE: I tried to solve the problem like described in Gamefreak0's answer, but it's not working. I created a ShadowChecker object and I check in a foreach-loop if it collides with a platform. If it collides, the variable ShadowPosition gets changed. But it's not working correctly. I made a picture, and in this picture you see that the shadow isn't on a platform. Why is the shadow not getting drawn on the pink platform?

The character(black rectangle) is above the pink platform, so the shadow should be on the pink platform.

Shadow isn't on the correct position

I update the shadow's position with this code:

 protected override void Update(GameTime gameTime)
    {         
        Rectangle CollisionRect = new Rectangle(-1000, -1000, 0, 0);
        ShadowChecker = CharacterRect;

        //FloorRect is the brown rectangle at the bottom. There is no other platform under the FloorRect.
        while ((CollisionRect.Height == 0) && (ShadowChecker.Bottom <= (FloorRect.Y + FloorRect.Height / 2)))
        {
            ShadowChecker.Y += 1;
            foreach (Rectangle r in PlatformRectangles)
            {
                if (ShadowChecker.Intersects(r))
                {
                    CollisionRect = r;
                    break;
                }
            }
        }
        if (CollisionRect.Height != 0)
        {
            // Draw the shadow on this position
            ShadowPosition = new Vector2(CharacterRect.X, CollisionRect.Y - CollisionRect.Height / 2);
        }

        base.Update(gameTime);
    }
\$\endgroup\$
  • \$\begingroup\$ well it's clearly positioning the shadow in line with the green tile. So that means the character rectangle is intersecting with that platform; are you sure your collision rectangles aren't just too wide? \$\endgroup\$ – jhocking Apr 3 '14 at 16:03
-2
+50
\$\begingroup\$

Try this approach:
Spawn a shadow object on the X and Y position of the player, then apply a very strong gravity force, and check for collisions (just like you do with the real player)
Then on the next frame update the coordinates of the shadow object with the new X and Y cooordinates of the player (I.E. place it on the same coordinates of the player) Then apply again gravity, etc.
It is important that you set a strong enough gravity, so that you're sure that your shadow does not float mid-air.

\$\endgroup\$
  • \$\begingroup\$ Thanx. It works. \$\endgroup\$ – Homer_Simpson Apr 2 '14 at 16:21
  • 1
    \$\begingroup\$ The basic approach sounds valid, but I would probably not spawn a new object each frame, but rather create one per player (or per object that should cast a shadow) and then update its position each frame with the player's/object's position. Reason: Creating and deleting objects each frame can result in a performance loss (e.g. due to memory allocation or construction logic). \$\endgroup\$ – Philip Allgaier Apr 3 '14 at 12:31
  • \$\begingroup\$ That's true, thanks for pointing it out. I'll edit the answer :) \$\endgroup\$ – Riccardo Vailati Apr 3 '14 at 14:57
  • 1
    \$\begingroup\$ I don't think using physics for this kind of thing is a very good idea, it's basically choosing complexity for the sake of laziness. It would be better to come up with a calculated approach like the answer from Janos. \$\endgroup\$ – MetaGuru Apr 3 '14 at 15:24
  • \$\begingroup\$ @RiccardoVailati It still says to spawn a new object each frame, right at the beginning. \$\endgroup\$ – Philip Allgaier Apr 3 '14 at 15:35
8
\$\begingroup\$

Draw a shadow sprite like you draw your character before the character (to make it appear behind him). Make sure its X position is updated according to the character, but the Y position stays on the tile. You have got a shadow.

\$\endgroup\$
  • \$\begingroup\$ The X position of the shadow is always correct, but I don't know how to calculate the right Y position. How can I do that? How can I check on which tile the shadow should be drawn? I have multiple platforms one above the other, they look like a house with multiple floors. My shadow is always drawn on the wrong platform. \$\endgroup\$ – Homer_Simpson Mar 24 '14 at 20:28
  • \$\begingroup\$ Somehow you should know on which platform you are standing (think about your collision detection). That is the platform that you need the Y position of. \$\endgroup\$ – János Turánszki Mar 24 '14 at 23:09
  • 1
    \$\begingroup\$ For example you could take the tiles which are close to the character on the X axis, then search for the tile with maximum height which is still under the character. \$\endgroup\$ – János Turánszki Mar 25 '14 at 8:27
  • 1
    \$\begingroup\$ You could also cast an infinite ray from the position of your character pointing downwards. First intersection with a tile along the ray determines the shadow's position. \$\endgroup\$ – János Turánszki Mar 28 '14 at 18:37
  • 1
    \$\begingroup\$ I agree with the ray-intersection method. This is accurate, and even allow you to cast tilted shadows by just changing the direction of the ray (Tilting shadow is probably a terrible game design Idea though except maybe for a hardcore level). Just look for 2D raytracing methods, and use the closest ray intersection as the center of the shadow. \$\endgroup\$ – Lærne Apr 1 '14 at 11:38
1
\$\begingroup\$

It may not be the best answer but,

Make an object called ShadowChecker that constantly draws a rectangle/line below the player. Use collision detection to adjust the height of the rectangle/line until it's not colliding with any object besides the player AND there is a platform just below the collision box. Then check the Y position just below the ShadowChecker, if there is a platform there, that's where you draw the shadow, if there isn't, don't draw the shadow. (the ShadowChecker's collision box is probably still updating if this happens)

This probably isn't the best answer but it'll give you some results.

EDIT: Method 2: Expanding on Predicting Collision Detection

Since the first method didn't work you'll have to create a method that does something similar to Collision Prediction. Here's a good article on it. Your method just needs to look only at the player's Y coordinate and always be looking down. Once you've figured out when the collision happens you know where it happens so you can draw the shadow there. If this doesn't work I would suggest Riccardo Vailati's answer.

\$\endgroup\$
  • \$\begingroup\$ I created a ShadowChecker object and I check in a foreach-loop if it collides with a platform. If it collides, the variable ShadowPosition gets changed. But it's not working correctly. You can see it in my question above. \$\endgroup\$ – Homer_Simpson Mar 28 '14 at 21:34
0
\$\begingroup\$

Alright, so an easyish change to your algorithm will make it both neater and probably work better:

  1. You use ShadowChecker as Character rect that you move downwards. Instead, expand it's height to either infinity or the bottom of the screen (FloorRect.Y).
  2. After expanding it, loop through the platforms:

        float currentHighestY = ShadowChecker.Y;
        foreach (Rectangle r in PlatformRectangles)
        {
            if (ShadowChecker.Intersects(r) && r.Y < currentHighestY)
            {
                currentHighestY = r.Y;
                CollisionRect = r;
                break;
            }
        }
    
  3. i.e. You're picking the "highest" intersecting platform and you're only looping through the platforms once.

  4. Next step after that is to limit the amount of platforms you check with, but I think that's probably out of the scope.

Another point that will help you more than anything ever has is: you should use debugger to figure these things out. While you're running your app in debug mode, put a break point at the start of the loop by pressing F9. After that, go step by step with F10 until you figure out what the problem is.

In this case, you might specifically want to put a break point on the

if (ShadowChecker.Intersects(r))

And see what the values of ShadowChecker and r are at the moment and why this returns true. This should take you closer to the root cause of your problem.

\$\endgroup\$
  • \$\begingroup\$ Should note that this approach allows you to remove the outer "while" loop completely. Furthermore, you can "narrow" the ShadowChecker rectangle as it only needs to be one pixel wide running through the center of your character. In fact, you could just replace the whole intersects with if(Character.X > r.X && Character.X < r.X + r.Width && r.Y < currentHighestY) \$\endgroup\$ – Muhwu Mar 30 '14 at 3:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.