Sign in to follow this  
Splinter of Chaos

Can you do transparent in SDL? Can I have single pixle acces? Why are my pointers 0?

Recommended Posts

I've found that if I asked every question I have, I'd either always have a new thread running, or I'd have my own and it would ALWAYS be at the top. Consequently, I only ask a question when my problem is directly impeding me from making any progress whatsoever. I have three questions I'd really liked answered. Can you do semi-transparencies in SDL? What's the point of having 32bit (like Lazy Foo tells me to) pixels when I only use 24? Can I have single pixel access? I ask because I want my particle system to spawn circles, not squares. Why are all my pointers NULL (runtime error)? I can't remember the last thing I changed, but I don't think it was connected. I think it was changing my rectangular vector class to be a template class, but then realizing how inconvenient that was and going back to how it was. Well, here's the source. The first file is the one with the problem. Character_Classes.h
/*****************************************************************
 Written by Scott Prager

 Slightly more advanced stuff on putting a sprite on screen.
*******************************************************************/
 
#include <stdlib.h> // For random numbers.
#include <time.h>
#include <iostream> // For testing.
#include <list>
#include <string>
using namespace std;

#include "SDL_work.h"

#include "Character.h" // The sub class.
#include "Particle_System.h"

// This is the class the player will play as. 
// 
// Things orbit the player, and he/she dies if something makes contact.
//
// The state system is a little different here.
// Instead of dying when it collides with another sprite, it'll become inactive,
// so the state will be (I). This way, you can watch enemies orbit it even after
// death. Also, there would be no more colliding with it.
class Player : public Character
{
    public:
        Mix_Chunk * dieFX;

        Player()
        {
            image = loadImage( "Art\\GravityMagnifier.bmp" );
            dieFX = Mix_LoadWAV( "FX\\PlayerDie.wav" );

            // BUG: image and dieFX are NOT LOADING. The pointers back come from the 
            // load functions NULL. HIGHEST PRIORITY. MUST FIX BEFORE CONTINUEING ON.

            // Set the position on the screen to the center.
            position.x =  screenWidth/2 - image->w/2;
            position.y = screenHeight/2 - image->h/2;

            velocity.magnitude( 0 );
            maxSpeed = 3.0f;

            spriteList.push_back( this );
        }

        ~Player()
        {
            SDL_FreeSurface( image );
            Mix_FreeChunk( dieFX );
        }



        void move()
        {
            // Just in case I ever decide to restrict this...
            if( !isMovable )
                return;

            // Stop all movement.
            velocity.magnitude( 0 );

            if( keyStates[SDLK_UP   ] )
                velocity.y -= maxSpeed;

            if( keyStates[SDLK_DOWN ] )
                velocity.y += maxSpeed;

            if( keyStates[SDLK_LEFT ] )
                velocity.x -= maxSpeed;

            if( keyStates[SDLK_RIGHT] )
                velocity.x += maxSpeed;

            position = position + velocity;
        }

        // Instead of raising the delete flag, when the player is hit,
        // she/he will become invisible.
        void collide( Character *crasher )
        {
            // If it's not a stopper that's stopped, it's probably deadly.
            if( isCollidable && crasher->isMovable && !keyStates[SDLK_i] )
            {
                isVisible = false;

                // Make rings.
                RectVector middle = center();
                explosion( 1000, center(), 4, 15, 10, WHITE, 1000 );
                explosion( 1000, center(), 4, 8,   4, WHITE, 1000 );
                explosion(  500, center(), 4, 3,   0, WHITE, 1000 );

                Mix_PlayChannel( -1, dieFX, 0 );
            }
        }
};
Player * player = NULL;

// The parent class of any character wishing to kill the player.
class Enemy : public Character
{
    public:
        Mix_Chunk * spawnFX;
        Mix_Chunk * dieFX;

        // The color of the sprite, and the subsequent explosions.
        SDL_Color color;

        // How strongly it is attracted to the player.
        int gravityMagnification;

        Enemy( string imageFile, string spawnFXfile, string dieFXfile, float MaxSpeed, int GravityMagnification )
        {
            image   = loadImage( imageFile );
            spawnFX = Mix_LoadWAV( spawnFXfile.c_str() );
            dieFX   = Mix_LoadWAV( dieFXfile.c_str() );

            // Play that "I'm alive now" sound.
            Mix_PlayChannel( -1, spawnFX, 0 );
            // Isn't it nice?

            // Find a good spot. Not too crowded. Good view. Stuff like that.
            findSpot();

            if( !player->isVisible )
            {
                // Add a little more orbit to the momentum.
                velocity.x = rand<float>( 2 );
                velocity.y = rand<float>( 2 );
            }
                
            maxSpeed = MaxSpeed;
            gravityMagnification = GravityMagnification;

            spriteList.push_back( this );
        }

        ~Enemy()
        {
            SDL_FreeSurface( image );
            Mix_FreeChunk( spawnFX );
            Mix_FreeChunk( dieFX );
        }

        
        void move( )
        {
            if( !isMovable ) 
                return;

            RectVector toTarget = player->center() - center();
            
            // Change it inversly to distance by its gavity magnification.
            toTarget = gravityMagnification / toTarget.magnitude();

            // Law of universal gravitation.
            //toTarget = 10( gravityMagnification / pow( toTarget.magnitude(), 2 );

            // Add the vector to the velocity.
            velocity = toTarget + velocity;
            // And make sure the velocity isn't too fast.
            velocity.clampMag( maxSpeed );

            // And affect the orbital.
            position = position + velocity;
        }

        // Finds a spot on the screen where it isn't to close
        // to anything.
        void findSpot()
        {        
            bool goodSpot;

            // Count how many times you go the loop.
            int count = 0;

            do
            {
                goodSpot = true;
                position.x = float( rand() % ( screenWidth-image->w) );
                position.y = float( rand() % (screenHeight-image->h) );

                // If it's not 200 from the player, it's bad,
                if( ( center() - player->center() ).magnitude() <= 150 )
                    goodSpot = false;

                // You it's too hard to find a spot, not being by the player is enough.
                if( count > 10 )
                    break;

                // Don't be so picky about the others.
                for( list<Character *>::iterator i=spriteList.begin();
                                                i != spriteList.end(); ++i)
                {
                    if( ( center() - (*i)->center() ).magnitude() <= 50 )
                        goodSpot = false;
                }
            }
            while( ++count, !goodSpot );

        }

        void collide( Character *crasher, int explosionSize, int particleSize=4 )
        {
            if( isAlive && isCollidable && crasher->isCollidable )
            {
                // Make the explosion.
                explosion( explosionSize, center(), particleSize, 15, 0, color, 1000 );
                // Here the explosion.
                Mix_PlayChannel( -1, dieFX, 0 );
                // Die.
                isAlive = false;
                // And be happy.
                // :)
            }
        }
};



// An enemy of the player.
//
// Orbits the player and kills him/her if succesful.
// If it its any other sprite, it dies.
class Orbital : public Enemy // number one
{
    public:
        Orbital()
            : Enemy( "Art\\Orbital.bmp", "FX\\OrbitalSpawn.wav", "FX\\OrbitalDie.wav", 20, 20 )
        {
            color = WHITE;
        }

        void collide( Character *crasher )
        {
            if( isAlive && crasher->isCollidable )
            {
                // Make the explosion.
                explosion( 600, center(), 4, 15, 0, WHITE, 1000 );
                // Here the explosion.
                Mix_PlayChannel( -1, dieFX, 0 );
                // Die.
                isAlive = false;
                // And be happy.
                // :)
            }
        }
};

// Orbits the player like the orbital, but a lot stronger.
class Bullet : public Enemy 
{
    public:
        Bullet()
            : Enemy( "Art\\Bullet.bmp", "FX\\BulletSpawn.wav", "FX\\BulletDie.wav", 20, 30 )
        {
            color.r = 0;
            color.g = 150;
            color.b = 15;

            // color.g should be 152, but for some reason, this causes some unintended blue
            // particles. Interesting?
        }

        void collide( Character *crasher )
        {
            if( isAlive && crasher->isCollidable )
            {
                // Make the explosion.
                explosion( 450, center(), 3, 20, 0, color, 1000 );
                // Here the explosion.
                Mix_PlayChannel( -1, dieFX, 0 );
                // Die.
                isAlive = false;
                // And be happy.
                // :)
            }
        }
};

// Weak orbit, but a difficult foe.
// When it's hit by a fellow enemy, it switches between stopped (where it doesn't move) and active.
// Normally it'll kill the player, but the only way to destroy it is for the player to hit it
// while it's stopped.
class Stopper : public Enemy
{
    public:
        Stopper()
            : Enemy( "Art\\Stopper_Active.bmp", "FX\\StopperSpawn.wav", "FX\\StopperDie.wav", 10, 10 )
        {
            color.r = color.g = color.b = 85;
        }

        void collide( Character *crasher )
        {
            if( !isCollidable || !crasher->isCollidable )
                return;

            if( isMovable )
            {
                image = loadImage( "art\\Stopper_Stopped.bmp" );
                // Make up for the change in picture.
                position.x += 1;
                position.y += 1;

                // While the bug with collision detection still exists, 
                // better keep this one under wraps.
                //
                // Draw the ring exploding to explain why it'll not be missing.
                //explosion( 500, center(), 7, 20, 20, color, 1000 );

                isMovable = false;

                // BUG: See change log (2007 Dec 28)
                position = position - velocity;

                // And zero velocity so you don't start moving if you reactivate.
                velocity = 0;

                return;
            }

            if( !isMovable )
            {
                if( crasher == player )
                {            
                    // Make the explosion.
                    explosion( 2000, center(), 7, 15, 0, color, 1000 );
                    // Here the explosion.
                    Mix_PlayChannel( -1, dieFX, 0 );
                    // Die.
                    isAlive = false;
                    // And be happy.
                    // :)
                }
                else
                {
                    image = loadImage( "Art\\Stopper_Active.bmp" );
                    // Make up for the change in picture size.
                    position.x -= 1;
                    position.y -= 1;

                    isMovable = true;
                }
            }
        }
};

I thought the problem might have something to do with how I set up SDL. SDL_work.h
/*****************************************************************
 Written by Scott Prager

 Contains some basic funtionality for working with images and SDL.
*******************************************************************/

#ifndef SDL_WORK
#define SDL_WORK

#include <stdlib.h> // For atexit()

#include "SDL/SDL.h" 
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h" 
#include "SDL/SDL_mixer.h"
#include "SDL/SDL_Timer.h"


// Set up some colors.
const SDL_Color BLACK = {   0,   0,   0 };
const SDL_Color RED   = { 255,   0,   0 };
const SDL_Color GREEN = {   0, 255,   0 };
const SDL_Color BLUE  = {   0,   0, 255 };
const SDL_Color WHITE = { 255, 255, 255 };

// The screen.
SDL_Surface *screen = NULL;
int screenWidth  = 640; 
int screenHeight = 480;

TTF_Font *regularFont = NULL;
SDL_Event SDLevent; 
Uint8 *keyStates = SDL_GetKeyState( NULL );

SDL_Joystick *joystick = NULL;
// Know the joystick's range.
const int JOYSTICK_RANGE = 32767;

// Set up the time.
const int SECOND = 1000;          // Number of ticks in a second.
const int FRAMES_PER_SECOND = 60; // The frames to be in one second.
const int TICKS_PER_FRAME = SECOND/FRAMES_PER_SECOND;

/* The function clears up everything that was created in this .h file. NOTHING ELSE. */
void finish(void)
{
    SDL_FreeSurface( screen ); 
}


/* Loads an image and optimizes it. */
SDL_Surface *loadImage( std::string fileName, bool optimize=true )
{
    SDL_Surface* loadedImage    = NULL; // The original image.
    SDL_Surface* optimizedImage = NULL; // The future optimized form.

    // Load the image. ( string.c_str() returns a const char* used for IMG_Load )
    loadedImage = IMG_Load( fileName.c_str() );

    // If it was a failure, the loaded image would still be NULL.
    // But if is succeded...
    if( loadedImage )
    {
        // If told to optimize...
        if( optimize )
        {
            optimizedImage = SDL_DisplayFormat( loadedImage );
        }
        // If not told to...
        else
        {
            optimizedImage = loadedImage;
        }

        // Free the old image. Graphics are huge. Always do this.
        SDL_FreeSurface( loadedImage );
    }

    return optimizedImage; // Return what we have come for.
}

/* Initialize SDL and all subsystems. Returning a bool to make sure it worked. */
bool initSDL( const int SCREEN_WIDTH = 640, const int SCREEN_HEIGHT = 480, const int SCREEN_BPP = 32 )
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 || TTF_Init() == -1) 
        return false; 

    // Initialize joystick controls.
    if( SDL_NumJoysticks() < 1 )
        return false;

    joystick = SDL_JoystickOpen(0);

    if( joystick = NULL )
        return false;

    // Set up the screen.
    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

    // If there was a problem initializing the screen, FAIL.
    if( screen == NULL ) 
        return false; 

    // Set up the audio.
    // frequency originally was 22050.
    if( Mix_OpenAudio(44100,MIX_DEFAULT_FORMAT,2,4096) == -1 ) 
        return false;

    // TODO set up the font and a joystick.

    // Get rid of what this header file's created when you leave.
    atexit( finish );

    // Finally, update the following globals.
    screenWidth  = SCREEN_WIDTH;
    screenHeight = SCREEN_HEIGHT;

    return true; // Made it this far, huh?
}

/* Blits one surface (source) to anoter (target) at an offset (x and y). */
void applySurface( int x, int y, SDL_Surface* source, SDL_Surface* target=screen, SDL_Rect* clip = NULL )
{
    // Make the offset.
    SDL_Rect offset;
    offset.x = x;
    offset.y = y;

    SDL_BlitSurface( source, clip, target, &offset );
}

Uint32 colorToUint32( const SDL_Color *color )
{
    // Initialize the end value.
    Uint32 endVal = 0x0;

    // And add Red, Green, Blue, and Alpha
                           // 0xAARRGGBB
    endVal += color->b      *        0x1;
    endVal += color->g      *      0x100;
    endVal += color->r      *    0x10000;
    endVal += color->unused *  0x1000000;

    return endVal;
}

void clearScreen()
{
    SDL_Rect srnClear;
    srnClear.x = 0;
    srnClear.y = 0;
    srnClear.h = screenHeight;
    srnClear.w = screenWidth;

    SDL_FillRect( screen, &srnClear, SDL_MapRGB(screen->format,0x0,0x0,0x0) );
}

#endif


The program goes straight from main (which just calls srand() ) to the game loop. The game loop calls the init function and the init function makes the player, which causes the error. Why? EDIT I just found out how to do the single pixel access. I really should finish the Lazy Foo tutorials. I sort of stopped after 15/36.

Share this post


Link to post
Share on other sites
Quote:

Can you do semi-transparencies in SDL? What's the point of having 32bit (like Lazy Foo tells me to) pixels when I only use 24?


SDL supports 2 methods for transparency: per-pixel and per-surface. Per-surface transparency is the faster of the two, but has the limitation of a uniform transparency value. Use SDL_SetAlpha() for this.

The other is enabling what SDL calls the "Source Alpha" flag on the surface. Without it, a 32bit surface is just a word aligned 24 bit surface. note: I think that is why SDL prefers to use 32bit surfaces, word aligned operations are apparently much faster than all the bit-twiddling needed to work on a 24bit surface. Enabling this flag also makes use of the SDL_SetAlpha() function, just with different parameters. note: SDL does not support hardware accelerated alpha blits. Mixing hardware surfaces and hardware acceleration is usually slower than software blending. For a very interesting read on SDL, I highly recommend Bob Pendleton's SDL articles.

Quote:

Can I have single pixel access? I ask because I want my particle system to spawn circles, not squares.


Pixel access is very slow. If you can avoid it try to do so. Even though you said you've found how to do this, I am compelled to link you to the idiomatic SDL pixel access functions, described in the SDL documentation wiki. The rest of the wiki is worth a look too.

Quote:

Why are all my pointers NULL (runtime error)?


Let SDL tell you why. Use the appropriate error function. For example, when SDL_LoadBMP() returns null, SDL_GetError() should tell you why. IMG_Load() reports errors through IMG_GetError() and Mix_Load*() reports through Mix_GetError() IIRC.

Share this post


Link to post
Share on other sites
    // If everything doesn't initialize, tell me why.
if( SDL_Init( SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) == -1 )
{
errors << "SDL Init error: " << SDL_GetError() << endl;
return false;
}



I set up an input file and it told me "No video mode has been set" and "Audio device hasn't been opened". So I went to the function that initializes them and it turns out that it's not making it past this line. I've already posted the source of this file so I won't do it again, but I really don't get it.

EDIT
Problem solved. I was stupid enough to say
    if( SDL_NumJoysticks() < 1 )
{
errors << "SDL Init error: " << SDL_GetError() << endl;
return false;
}

Then, earlier today, my sister unplugged my game pad.

Although, I forgot to respond about the accessing pixels being slow...
So, if my plan was to decide how intensely to color each pixel based on the distance from the center of the particle, would this likely be a bad idea? Were you trying to tell me to switch to OpenGL by sending me that link?

[Edited by - Splinter of Chaos on December 30, 2007 10:26:57 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Splinter of Chaos
Although, I forgot to respond about the accessing pixels being slow...
So, if my plan was to decide how intensely to color each pixel based on the distance from the center of the particle, would this likely be a bad idea? Were you trying to tell me to switch to OpenGL by sending me that link?


No, I was not telling you to switch to OpenGL. It is an option though, especially if you plan to use lots of alpha blending to achieve special effects.

Pixel access is slow. However, that doesn't mean anything by itself. If you are smart, you can reduce the amount of raw pixel accesses you do. For example, you can allocate a SDL_Surface for the particles, and share it between them (maybe using boost::shared_ptr<>) so that you don't need to create it over and over. Of course, if each particle has a different size then maybe you cannot do this.

It all depends. For small surfaces pixel access probably won't be too slow. The only way to know for sure is to try.

Share this post


Link to post
Share on other sites
I'm actually working on the text in the game right now, so it's all hypothetical at the moment.

I wanted to do something really impressive here, so I have the position held on two floats and I was going do that distance thing I said. I was going to even do this to one pixel sized particles. If it was between four pixels, you'd actually see 50% transparent pixels on each four. Then if it was all on one, you'd see 0% transparency there. I would consider the pixel's x + 0.5 and the pixel's y + 0.5 to be the center.

So, as I said, I'm working on the text right now, but if you think I need/strongly suggest OpenGL for this...I've been contemplating the need to switch for about a day now. Especially because I want to be very liberal on gratuitous use of graphics.

I have enemies spawning and dying frequently, and when they die, they will spew from 600 to 2000 particles. This might seem like a bit much, but it's an awesome reward system to start out with a bare background and fill it as the game goes on. Plus, I've never seen a death screen as entertaining as mine. (I've waisted away twenty minutes just staring at it.) So, I guess optimization in key here. Especially because I found if I let the game run on its own, it eats all my virtual memory.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this