March 28, 2024, 03:06:45 AM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Frags example.

Started by Ionic Wind Support Team, May 17, 2006, 05:11:19 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Ionic Wind Support Team

Not just a 2D example, but an example showing one of the many ways you can structure an application in Aurora.  In this program there is a main class called TheApp.  TheApp class contains the variables and functions required to make everything work.  The main() function consists of only two lines...create an instance of TheApp and pass control to it's Run() member function.

This is very similar to how MS Visual Studio creates applications.

Anyway enjoy the pretty colors ;)


/*
The frags demo.  Hold down the left mouse button and move around to generate the fragments.
Press ESC to exit.
For Aurora Alpha 3 or greater
*/
#define width 640
#define height 480
#define intensity 20

struct Frag {
float x;
float y;
float xs;
float ys;
int r;
int g;
int b;
}

class TheApp
{
declare TheApp();
declare _TheApp();
declare CreateFrags(int mx,int my);
declare UpdateFrags();
declare RenderFrags();
declare Run(),int;
//variable of the app
C2DScreen screen;
CPointerList frags;
float gravity;
int ptx,pty,speed;
}

TheApp::TheApp()
{
gravity = .1f;
temp = 0;
ptx = 0;
pty = 0;
done=0;
speed = 0;
//create the linked list of frags.
frags.Create();
}

TheApp::_TheApp()
{
frags.RemoveAll(true);
}

TheApp::Run(),int
{
//Create the screen
if(screen.CreateFullScreen(width,height,32) < 0)
{
MessageBox(0,"Couldn't creat the screen..sorry","Error");
return false;
}
//turn off the cursor
screen.SetCursor(CS_CUSTOM,0);
//get a pointer to the back buffer
C2DSurface *back = screen.GetBack();
do
{
back->Fill(0);
UpdateFrags();
ptx = screen.MouseX();
pty = screen.MouseY();
if( GETKEYSTATE(0x01) )
CreateFrags(ptx,pty);
back->Lock();
back->DrawRect( ptx,pty-3,1,7,RGB(255,255,255) );
back->DrawRect( ptx-3,pty,7,1,RGB(255,255,255) );
RenderFrags();
back->Unlock();
screen.WriteText(4,20,"FPS: " + NumToStr(speed,0) + " Esc to exit");
speed = screen.Flip(1);

}Until GetKeyState(0x1B);
screen.CloseScreen();
return true;
}

TheApp::UpdateFrags()
{
void *pos = frags.GetFirst();
while pos
{
Frag *f = frags.GetData(pos);
f->x += f->xs;
f->y += f->ys;
f->ys += gravity;
If((f->x < 0) || (f->x >= width) || (f->y >= height))
{
pos = frags.Remove(pos,TRUE);
f = NULL;
}
Else If (f->b > 0)
{
f->b -= 5;
}
Else If (f->g > 0)
{
f->g -= 3;
}
Else If (f->r > 0)
{
f->r -= 1;
If f->r=0
{
pos = frags.Remove(pos,TRUE);
f = NULL;
}
}
if f <> 0
pos = frags.GetNext(pos);
}
}

TheApp::CreateFrags(int mx,int my)
{
int count = RAND(intensity)+intensity;
float an,anstep = 360.0f/count;
an=RND(anstep);
for(int k=1;k <= count; k++)
{
Frag *f = frags.Add(NEW(Frag,1));
f->x=mx;
f->y=my;
f->xs=FCosD(an) * (Rnd( 1 ) + 3f);
f->ys=FSinD(an) * (Rnd( 1 ) + 3f);
f->r=255;
f->g=255;
f->b=255;
an += anstep;
}
RETURN;
}

TheApp::RenderFrags()
{
C2DSurface *back = screen.GetBack();
void *pos = frags.GetFirst();
while(pos)
{
Frag *f = frags.GetData(pos);
back->DrawRect( f->x-1,f->y-1,3,3,RGB(f->r,f->g,f->b) );
pos = frags.GetNext(pos);
}
}

global sub main ()
{
TheApp app;
app.Run();
}
Ionic Wind Support Team

Doc

Cool! ...would make a really nice screen saver. :)

-Doc-

kryton9

Thanks for the sample! Fun to play with.

Trying the gravity at .01f  really cool effects depending on the length of the mouse being held down and moved around.

But I am lost reading the code. This class stuff is fun, if you look at it as a challenge. So many myapps defined, declared assigned used. I guess when I understand this I will get the hang of this class stuff.

What I don't get is the classes are in the source file but when I use the class viewer I don't see them in there? And in other examples I see the resources in the resource viewer but I get errors compiing with the linker and resources, Peaslee's Skeleton example is one for example.


Ionic Wind Support Team

Class Viewer isn't finished yet. 
Ionic Wind Support Team

kryton9

ahh, glad to hear that as I thought I was really out of touch in not getting it to work :)  Now I am inspired to keep reading the samples and figuring it out, thanks for a great example to look at though and play with. It is really fast.

Ionic Wind Support Team

Ionic Wind Support Team

Protected

Just a warning - I have alpha 3 rev 1 and this code does not compile here. Maybe it requires rev 2? I can't test that just yet, I want to spend my rev 1 trial entirely before moving to 2 :P

(Can't pay the license yet)

EDIT: A clarification: The compiler goes into infinite cycle when compiling it.

Ionic Wind Support Team

Should work on Rev 1.  Don't know as we are way past that ;)
Ionic Wind Support Team

Haim

I was playing with the frags example, trying to understand the TheApp class. I am just beginning oop, so my questions are probably stupid. my apologies for that...

1. In the example, the variable app, which is an instance of TheApp is defined in sub main. Why,  when I moved it outside of main (like a global variable), the program would not run, resulting in a GPF.
It seems as if variable app is not "recognized" from outside main()

2. I created an application with a CApp class, similar to the one in the frags example. The class was instantiated in main(), like in the frags example.
I also created  class MyWin, derived from Cwindow and put an instance of it as a member variable of CApp.
I was unable to access CApp from MyWin.
Is it possible to access CApp from the MyWin class (which is a member of CApp?)


Haim


Parker

You can't create a class globally, because then the compiler would have to put the constructor call somewhere, and if that module doesn't have main() inside it, that would be almost impossible.

You can place class variables in a function body or inside another class, and you can put a pointer to a class anywhere and manually new/delete it.

You can't really do anything with a class right now, much more than the MyClass!!SomeMethod to forward processing, but if you have a member variable, you should be able to do something like m_theApp.Run();.

Haim

Thanks for the explanation.
Haim

Ionic Wind Support Team

Just to clarify.  You can use global classes, but the compiler doesn't call the default constructor currently.  You can call the constructor yourself though.

#2.  by a bit of class magic you can accomplis this.  In other OOP languages they use a global pointer.


class MyAPP
{
   declare Run();
   //app variables
   MyWin win;
   int some_variable;
}

global gpMyApp;
MyAPP *gpMyApp;  //a global pointer to the app class

global sub main()
{
    MyApp app;
    gpMyApp = app;
    app.Run();
}



The from a method of MyWin you can use this global pointer.


MyWin::OnCreate(),int
{
gpMyApp->some_variable = 2;
}


I should note that the frags example shows only one of the many ways to structure an OOP application.  Having an "App" class that is the super class of the application tends to make the program a bit easier to manage.  You don't need to use it obviously. 

Paul.
Ionic Wind Support Team

Haim

Wow!ÂÃ, Got it.
Thanks,

Haim

Protected

I'm learning a lot from this example, but I have a question about the creation of instances:

global sub main ()
{
TheApp app;
app.Run();
}


Frag *f = frags.Add(NEW(Frag,1));

This is confusing me a little. You use NEW for a struct? What exactly does it do? Does the creation of instances of classes not require NEW? Are they automatically created when you declare a variable whose type is that class? Are they automatically destroyed as well? If not, how are they destroyed? I've been looking mostly at screens and they seem to have their own methods but I'm not sure all classes work the same, or what's the standard behavior. In the meantime I'll keep poking around :P

Parker

New can be used on any variable type - struct, class, intrinsic. It calls __NEW which allocates memory for that item, and if what you're calling it on is a class, it calls the constructor for that class.

Local class variables are constructed where they appear, globals aren't because global code isn't actually executed like it is in BASIC.

Protected

Hm, I think I get it, thanks. What happens if I construct a class twice, then? Any invisible effects? Memory leakage and stuff?

Parker

Just the same memory loss as you normally would have, unless the class does some operation and then automatically deletes itself.

The code pSomeClass = new( SomeClass, 1 ); does this:
pSomeClass = calloc( 1, sizeof( SomeClass ) );
SomeClass::SomeClass( pSomeClass );
where the constructor's parameter is the "this" pointer. So if you call new twice in a row without deleting it, you're passing a new instance to the constructor each time, since the value returned from calloc (or in Aurora's case GlobalAlloc) will be different each time.

Protected

SO if you go:

pSomeClass = new( SomeClass, 1 );
pSomeClass = new( SomeClass, 1 );

(for example, no one would do anything so stupid, of course)

one instance of the class would be created and then lost (have no pointer to it), right? Would it be destroyed or would it be needlessly using memory?

Mike Stefanik

If you use new then the object (intrinsic type, structure or class) is created on the process heap and that object persists beyond the method that it is created in. You release the memory allocated from the heap using delete. For example, let's consider a simple class and a function to create an instance of an object and initialize it:


class CMyClass
{
ÂÃ,  ÂÃ,  declare CMyClass();
ÂÃ,  ÂÃ,  declare MyFunction(), int;

ÂÃ,  ÂÃ,  int m_nValue1;
ÂÃ,  ÂÃ,  int m_nValue2;
}

CMyClass::CMyClass()
{
ÂÃ,  ÂÃ,  m_nValue1 = 0;
ÂÃ,  ÂÃ,  m_nValue2 = 0;
}

CMyClass::MyFunction(), int
{
ÂÃ,  ÂÃ,  return (m_nValue1 + m_nValue2);
}

sub CreateInstance(int nParam1, int nParam2), pointer
{
ÂÃ,  ÂÃ,  CMyClass myClass;

ÂÃ,  ÂÃ,  // Initialize the instance of CMyClass
ÂÃ,  ÂÃ,  myClass.m_nValue1 = nParam1;
ÂÃ,  ÂÃ,  myClass.m_nValue2 = nParam2;

ÂÃ,  ÂÃ,  return &myClass;
}


This would be the wrong way to do this because the instance of CMyClass (named "myClass") is being created locally on the stack and it goes out of scope as soon as the function returns. In other words, the pointer that is being returned will no longer point to a valid object.

The correct way to implement that code would be as:


class CMyClass
{
ÂÃ,  ÂÃ,  declare CMyClass();
ÂÃ,  ÂÃ,  declare MyFunction(), int;

ÂÃ,  ÂÃ,  int m_nValue1;
ÂÃ,  ÂÃ,  int m_nValue2;
}

CMyClass::CMyClass()
{
ÂÃ,  ÂÃ,  m_nValue1 = 0;
ÂÃ,  ÂÃ,  m_nValue2 = 0;
}

CMyClass::MyFunction(), int
{
ÂÃ,  ÂÃ,  return (m_nValue1 + m_nValue2);
}

sub CreateInstance(int nParam1, int nParam2), pointer
{
ÂÃ,  ÂÃ,  CMyClass *pMyClass = new(CMyClass, 1);

ÂÃ,  ÂÃ,  // Initialize the instance of CMyClass
ÂÃ,  ÂÃ,  pMyClass->m_nValue1 = nParam1;
ÂÃ,  ÂÃ,  pMyClass->m_nValue2 = nParam2;

ÂÃ,  ÂÃ,  return pMyClass;
}


This is correct because the instance of the object is being created on the heap, and it will persist after the CreateInstance function returns. Once the instance of the CMyClass object is no longer required, you'd release it using delete. For example:


CMyClass *pClass = null;
int nResult = 0;

pClass = CreateInstance(10, 20);
if (pClass != null)
{
ÂÃ,  ÂÃ,  nResult = pClass->MyFunction();
ÂÃ,  ÂÃ,  delete pClass;
}

print("The result is ", nResult);

Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Mike Stefanik

May 20, 2006, 02:54:14 PM #19 Last Edit: May 20, 2006, 02:55:55 PM by Mike Stefanik
Quote from: Protected on May 20, 2006, 02:19:31 PM
SO if you go:

pSomeClass = new( SomeClass, 1 );
pSomeClass = new( SomeClass, 1 );

(for example, no one would do anything so stupid, of course)

one instance of the class would be created and then lost (have no pointer to it), right? Would it be destroyed or would it be needlessly using memory?

In languages like Java and C#, which have garbage collection, it would be automatically destroyed when the reference count to that object dropped to 0. In languages like C++ and Aurora, you would have to explicitly call delete between those two calls, otherwise you "lose" the first instance of the object and it won't be destroyed until the process terminates (in other words, you would have a memory leak).

In short, for every call to new, you must have a matching call to delete.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Protected

Aha. So if there is a class inside another class - or in other words, a class whose instance is automatically created - and I call NEW on it afterwards, it would also cause a problem like this, right?

EDIT: Call NEW on it only once.

Mike Stefanik

You can actually declare an instance an inner class without needing to allocate it dynamically, since the outer class that contains it is being allocated on the heap. For example, this is perfectly fine:


class CInnerClass
{
    declare CInnerClass();
    declare _CInnerClass();
    declare DoSomething();
}

CInnerClass::CInnerClass()
{
    writeln("The constructor for CInnerClass has been called\n");
    return;
}

CInnerClass::_CInnerClass()
{
    writeln("The destructor for CInnerClass has been called\n");
    return;
}

CInnerClass::DoSomething()
{
    writeln("The DoSomething method for CInnerClass has been called\n");
    return;
}

class COuterClass
{
    declare COuterClass();
    declare _COuterClass();
    declare DoSomething();

    CInnerClass m_innerClass;
}

COuterClass::COuterClass()
{
    writeln("The constructor for COuterClass has been called\n");
    return;
}

COuterClass::_COuterClass()
{
    writeln("The destructor for COuterClass has been called\n");
    return;
}

COuterClass::DoSomething()
{
    writeln("The DoSomething method for COuterClass is calling CInnerClass::DoSomething\n");
    m_innerClass.DoSomething();
    return;
}

global sub main
{
    COuterClass *pClass = new(COuterClass, 1);
    if (pClass != null)
    {
        pClass->DoSomething();
        delete pClass;
    }

    while (GetKey() == "");
    return;
}

Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Mike Stefanik

You could also implement that as:


class CInnerClass
{
    declare CInnerClass();
    declare _CInnerClass();
    declare DoSomething();
}

CInnerClass::CInnerClass()
{
    writeln("The constructor for CInnerClass has been called\n");
    return;
}

CInnerClass::_CInnerClass()
{
    writeln("The destructor for CInnerClass has been called\n");
    return;
}

CInnerClass::DoSomething()
{
    writeln("The DoSomething method for CInnerClass has been called\n");
    return;
}

class COuterClass
{
    declare COuterClass();
    declare _COuterClass();
    declare DoSomething();

    CInnerClass *m_pInnerClass;
}

COuterClass::COuterClass()
{
    writeln("The constructor for COuterClass has been called\n");
    m_pInnerClass = new(CInnerClass, 1);
    return;
}

COuterClass::_COuterClass()
{
    if (m_pInnerClass)
        delete m_pInnerClass;

    writeln("The destructor for COuterClass has been called\n");
    return;
}

COuterClass::DoSomething()
{
    writeln("The DoSomething method for COuterClass is calling CInnerClass::DoSomething\n");
    m_pInnerClass->DoSomething();
    return;
}

global sub main
{
    COuterClass *pClass = new(COuterClass, 1);
    if (pClass != null)
    {
        pClass->DoSomething();
        delete pClass;
    }

    while (GetKey() == "");
    return;
}


If you run this example and the previous one, you'll notice a subtle difference. The advantage of creating the inner class dynamically is that it gives you control over when the instance of the inner class object is created and destroyed. Otherwise, as in the previous example, it's handled automatically.
Mike Stefanik
www.catalyst.com
Catalyst Development Corporation

Protected

Oooooh, pointers... I see... I didn't have to deal with such things in Java ;P

I understand now, this is all very flexible! I'll have fun playing with it in the next days, I guess  ;D Thanks for the examples!

Parker

Because java only works with references and has a garbage collector. It works well with an interpreted language like Java, but generally you use a compiled language for speed and versatility, not hiding you from the real details and extra code injected into your program.