Skip to main content
gamedev.net
🔒 Locked 🎮 Unity

Help me spot the problem?

Started by BrknPhoenix Aug 2, 2006 at 10:27 PM 8 replies 1.1k views
Original Post
BrknPhoenix
BrknPhoenix
Alright, I've been looking at this so long without solving the problem that obviously my eyes aren't going to do it. I started out using an array to hold some textures, then a vector, and now I'm using the best solution, a circular linked list. However none of these changes are actually making this work. What is supposed to happen: This is for a character select screen. There is a large circle, in which the currently selected character appears. Around this large circle are smaller circles, each containing a face of a character. The user spins these faces around the large circle to select a character by pushing left and right. When the circle spins, it spins a number of degrees equal to the space between the outside circles. After making this partial spin, it reverts back to this original position. At the same moment in time that the circle reverts back to its original position, each face texture is moved one position around the circle. While the entire circle is never actually spun more than the distance between circles before it reverts, the illusion that it is spinning around seamlessly is created when the faces shift positions over one when the spin is complete. By moving the seek pointer in the linked list while drawing, it will draw the next face as it moves around the big circle drawing the smaller ones. What actually happpens with this code: It spins fine. That's all good. The problem arises with the face textures changing. Actually, it's not even getting that far. Let's say the circular linked list that contains the character faces has four faces in it. That's four textures. However, the first texture (index 0) is being displayed for ALL of the circles. Where there should be four textures containing textures index 0, 1, 2, and 3, instead there are four textures each being taken from index 0! The result: All four faces around the circle are the same. A note about the code: This is currently a little messy and not complete. There aren't actually four different faces, only two. But they alternate so it's easy enough to see that it's not working right. Just ignore some of the small test things and improper code. What I'm looking for is the problem in the logic that is causing this to occur. I'm posting the source for all the possible sources of the problem, because honestly, I don't know where it is. Following the code: 1. In the charselcircle constructor, textures are loaded into my texture classes, and the circular linked list is populated with the faces from the SmallFaces texture object. 2. The Update() function is called in the game logic. It figures out all the angles for drawing, determines if the player pushed a direction, and so forth. It moves the angle to draw at if the circle is spinning, stops it if it should stop, and increments or decrements the seek pointer in the circular linked list accordingly. 3. The Draw() function is called afterwards outside of the logic, where all the drawing is done. It takes the calculated angles and, using a for loop, draws all of the smaller circles evenly spaced around the larger circle. Within the loop, the circular linked lists seek pointer is incremented after a texture is pulled from it so the next texture will be used on the next loop. The end result should be each different texture being used once. CLL.H - The circular linked list

#ifndef CLL__H
#define CLL__H

template <class T>
class cllNode
{
    public:
        cllNode<T>* pNext;
        cllNode<T>* pPrev;
        T           value;
};
 
template <class T>
class CircularLinkedList
{
    public:
        CircularLinkedList()
        {
            pStart = 0;
            pEnd   = 0;
            pSeek  = 0;
        }
        
        ~CircularLinkedList();
        
        cllNode<T>* pStart;
        cllNode<T>* pEnd;
        cllNode<T>* pSeek;
        
        void        Add(T);     
        T           Get();       
        void        SeekForward();
        void        SeekBack();    
};

template <class T>
CircularLinkedList<T>::~CircularLinkedList()
{
    cllNode<T>* pNode;          // This node steps through the array
    cllNode<T>* pNxt;           // Used to store the next node up for deletion
    
    pNode = pStart;             // We start from the beginning
    
    while (pNode)               // We delete everything in this loop
    {
        pNxt = pNode->pNext;    // Store the next address
        delete pNode;           // Delete current node
        pNode = pNxt;           // Move up
    }
}

template <class T>
void CircularLinkedList<T>::Add(T new_value)
{
    cllNode<T>* pNew;           // This is our new node

    pNew = new cllNode<T>;      // Allocate memory for it
    pNew->value = new_value;    // Store the value
    
    if (!pStart)                // If there's no nodes...
    {
        pNew->pPrev = pNew;     // The new node is the only node,
        pNew->pNext = pNew;     // so the first, last, next, and
        pStart      = pNew;     // previous are all this node.
        pEnd        = pNew;
        pSeek       = pNew;
    }
    else                        // If there are nodes...
    {
        pNew->pPrev = pEnd;     // The last node is this node's previous
        pNew->pNext = pStart;   // The first node is this node's next
        pEnd        = pNew;     // The last node is now this node
    }
}

template <class T>
T CircularLinkedList<T>::Get()
{
    return pSeek->value;        // Return the value at the seek pointer
}

template <class T>
void CircularLinkedList<T>::SeekForward()
{
    pSeek = pSeek->pNext;       // Move the seek pointer forward
}

template <class T>
void CircularLinkedList<T>::SeekBack()
{
    pSeek = pSeek->pPrev;       // Move the seek pointer backward
}


#endif

CHARSELCIRCLE.H - Character select circle specification

#ifndef CHARSELCIRCLE__H
#define CHARSELCIRCLE__H

#include <allegro.h>
#include <alleggl.h>

#include "cll.h"
#include "texture.h"

#define SEL_CIRC_FILE       "charselcircle.tga"
#define FEMALE_MAGUS_FILE   "f_magus_c.tga"
#define FEMALE_SOLDIER_FILE "f_soldier_c.tga"

#define NUMBER_OF_TYPES     4.0f
#define NUMBER_OF_TYPES_i   4
#define SMALL_CIRC_DEGS     39.0f
#define SPIN_SPEED          1.0f

#define F_MAGUS_S_FILE      "f_magus_s.tga"
#define F_SOLDIER_S_FILE    "f_soldier_s.tga"

#define PI                  3.1415f

#define SMALL_FACE_SIZE     75
#define CIRCLE_SIZE         200.0f
#define CIRCLE_X            170.0f
#define CIRCLE_Y            166.0f

class CharSelCircle
{
    private:
        Texture             Textures;
        Texture             SmallFaces;
        Texture             Characters;
        bool                spinning_cw;
        bool                spinning_ccw;
        int                 selected;
        float               circ_x;
        float               circ_y;        
        float               starting_degrees;
        CircularLinkedList<GLuint> small_face_textures;
    public:
                            CharSelCircle();
                           ~CharSelCircle();
               
        void                Update();
        void                Draw();
};

#endif


CHARSELCIRCLE.CPP

#include <allegro.h>
#include <alleggl.h>
#include <cmath>

#include "charselcircle.h"
#include "cll.h"
#include "texture.h"

CharSelCircle::CharSelCircle()
{
    spinning_cw = false;
    spinning_ccw = false;
    face_order_needs_update = true;
    selected = 0;
    starting_degrees = SMALL_CIRC_DEGS;
    
    Textures.Load("select_circle", SEL_CIRC_FILE);
    
    Characters.Load("female_magus", FEMALE_MAGUS_FILE);
    Characters.Load("female_soldier", FEMALE_SOLDIER_FILE);    
    
    SmallFaces.Load("f_magus_s", F_MAGUS_S_FILE);
    SmallFaces.Load("f_soldier_s", F_SOLDIER_S_FILE);
    SmallFaces.Load("f_soldier_s", F_MAGUS_S_FILE);
    SmallFaces.Load("f_soldier_s", F_SOLDIER_S_FILE);  
    
    small_face_textures.Add(SmallFaces.At(0));
    small_face_textures.Add(SmallFaces.At(1));
    small_face_textures.Add(SmallFaces.At(2));    
    small_face_textures.Add(SmallFaces.At(3));    
}

CharSelCircle::~CharSelCircle()
{
    /* Destructor */
}

void CharSelCircle::Update()
{
    //TEST CONTROLS
    if ((!spinning_cw) && (!spinning_ccw))
    {
        if (key[KEY_LEFT])
        {
            spinning_cw = true;           
        }
        
        if (key[KEY_RIGHT])
        {
            spinning_ccw = true;            
        }
    }    

    // What to do if we're spinning clockwise
    if (spinning_cw)
    {
        // Do the actual spin
        starting_degrees += SPIN_SPEED;
        
        // If the spin is complete, reset everything and change the selection
        if (starting_degrees >= SMALL_CIRC_DEGS + (360.0f / NUMBER_OF_TYPES))
        {
            starting_degrees = SMALL_CIRC_DEGS;
            spinning_cw = false;
            selected += 1;
            small_face_textures.SeekForward();
            
            // Keep the selection value in bounds
            if (selected >= (int)NUMBER_OF_TYPES)
            {
                selected = 0;
            }            
        }
    }
    
    // What to do if we're spinning counter-clockwise
    if (spinning_ccw)
    {
        // Do the actual spin
        starting_degrees -= SPIN_SPEED;
        
        // If the spin is complete, reset everything and change the selection
        if (starting_degrees <= SMALL_CIRC_DEGS - (360.0f / NUMBER_OF_TYPES))
        {
            starting_degrees = SMALL_CIRC_DEGS;
            spinning_ccw = false;
            selected -= 1;
            small_face_textures.SeekBack();
            
            // Keep the selection value in bounds
            if (selected < 0)
            {
                selected = (int)NUMBER_OF_TYPES - 1;
            }            
        }
    }    
}

void CharSelCircle::Draw()
{
    // Get a clean slate of attributes
    glPushAttrib(GL_ENABLE_BIT);                    
        
    for (float circ_angle = starting_degrees; circ_angle <= 360.0f + starting_degrees; circ_angle += (360.0f / NUMBER_OF_TYPES))
    {                 
        circ_x = CIRCLE_SIZE * cos(circ_angle * (PI / 180.0f));
        circ_y = CIRCLE_SIZE * sin(circ_angle * (PI / 180.0f));
    
        // Set up to draw
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);
        glDisable(GL_DEPTH_TEST);       // Needed for transparency
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                     
        glBindTexture(GL_TEXTURE_2D, small_face_textures.Get());
        small_face_textures.SeekForward();

    
        // White polygon
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
        
        // Draw it
        glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 0.0f); glVertex2f((circ_x - (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_X, (circ_y + (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_Y);
            glTexCoord2f(1.0f, 0.0f); glVertex2f((circ_x + (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_X, (circ_y + (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_Y);
            glTexCoord2f(1.0f, 1.0f); glVertex2f((circ_x + (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_X, (circ_y - (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_Y);
            glTexCoord2f(0.0f, 1.0f); glVertex2f((circ_x - (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_X, (circ_y - (SMALL_FACE_SIZE / 2.0f)) + CIRCLE_Y);
        glEnd();                
    }
    
    glPopAttrib();    
}


brandonman
brandonman
I didn't read through this, but one thing might be that each circle on the outside should have a different name if they don't. just a guess.
BrknPhoenix
BrknPhoenix
Quote:
Original post by brandonman
I didn't read through this, but one thing might be that each circle on the outside should have a different name if they don't. just a guess.


The textures are in a linked list, so they don't even have names, just addresses. The textures with names are being loaded through my texture class. They don't actually need the names, but my texture class requires it. It's just there to convert the textures from .tga to GLuint. After that they get put in the circular linked list. There's a pointer in the linked list that points to the currently selected face. It should draw that one first, then when the seek pointer is moved up it just draws the next one. It's set up so that the number of circles drawn is equivalent to the number of total textures, so the end result should be they each get drawn once and it ends up where it started. Since it's circular it should be all nice and automatic and not need any names.

It's not working out nice though :(
brandonman
brandonman
strange. sounds like it outta works.
BrknPhoenix
BrknPhoenix
Quote:
Original post by brandonman
strange. sounds like it outta works.



Indeed... I really have no idea why it isn't working. I've gone over it by eye, on paper, in my head, several times over and over and I can't for the life of me figure out why this code doesn't work exactly like I was intending >_<
Tessellator
Tessellator
I've had a brief look at it and can't see anything obviously wrong either. Have you tried running it through the debugger (sorry if that's an obvious question :))?

I see you have SmallFaces declared like:

Texture SmallFaces;

Yet, SmallFaces seems to be used as some kind of list structure? What is a Texture? Perhaps SmallFaces.At(n) always returns SmallFaces(0)?

Good luck with the bug hunting - i hate the ones like this!
T

dmail
dmail
I've just been looking through your code can you tell me what CharSelCircle::selected's purpose is, the comment says "Keep the selection value in bounds"

Also have you put some break points in and stepped throught the code?
dmail
dmail
ok after having another look at it I think this is your problem
    if (!pStart)                // If there's no nodes...    {        pNew->pPrev = pNew;     // The new node is the only node,        pNew->pNext = pNew;     // so the first, last, next, and        pStart      = pNew;     // previous are all this node.        pEnd        = pNew;        pSeek       = pNew;    }    else                        // If there are nodes...    {        pNew->pPrev = pEnd;     // The last node is this node's previous        pNew->pNext = pStart;   // The first node is this node's next        pEnd        = pNew;     // The last node is now this node    }

What you have is a "non normal" circular buffer (I will post what I would call normal if you wish). The problem is if pStart is valid. Try changing that part to
    else                        // If there are nodes...    {        pEnd->pNext = pNew;     //point the previous node to this node        pNew->pPrev = pEnd;     // The last node is this node's previous        pNew->pNext = pStart;   // The first node is this node's next        pEnd        = pNew;     // The last node is now this node    }

I havn't check it but this should fix it.


edit:
Because of the way you have this you may also need to add
pStart->pPrev = PEnd;
dmail
dmail
This is what I meant by a normal circular buffer, not tested but should work

#include <vector>#include <cassert>template<typename T>class CircularLinkedList{public:	CircularLinkedList()	{		m_current = 0;	}	~CircularLinkedList(){}	void Add(T t)	{		m_list.push_back(t);	}	T Get()	{		assert(m_list.size() != 0);		return m_list[m_current];	}	void SeekForward()	{		if(m_list.size() == 0)return;		m_current = ++m_current % (int)m_list.size();	}	void SeekBack()	{		if(m_list.size() == 0)return;		if(m_current==0){m_current = m_list.size()-1;}		else {m_current = --m_current % (int)m_list.size();}	}private:	std::vector<T> m_list;	int m_current;};#endif

edit added check if forward

[Edited by - dmail on August 3, 2006 7:16:46 PM]
BrknPhoenix
BrknPhoenix
Thanks for the help! The linked list was indeed flawed and now it's all fixed up. But I finally found the MAJOR problem that was preventing anything from working properly at all. It was making my program crash when I used an array or vector to do this, and weird glitches when I used the list. But I finally found it...

It was this line:
    for (float circ_angle = starting_degrees; circ_angle <= 360.0f + starting_degrees; circ_angle += (360.0f / NUMBER_OF_TYPES))


more specifically, this part, the condition:

circ_angle <= 360.0f + starting_degrees;


Because of that, not only would it draw all the circles it was supposed to, but when it had gone full circle and arrived back at the degree it started at, it drew one more. Drew it directly on top of the first so you couldn't tell there were two. So in reality when I told it to draw 4 circles I thought it was, but it was actually drawing 5.

The Solution:

circ_angle < 360.0f + starting_degrees;


Yep... it was a friggin' equals sign that was the grand cause of all this mess.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.