Strategic RPG

Hola a todos! (Hi to everyone!)

I’m the everlasting n00b coming all the way from Argentina (Yes, it’s as sad as you can imagine) to ask you a few questions, since six days ago I didn’t know neither Python, Panda nor Blender :stuck_out_tongue:
(Although I know some C++, 3DMax and GameMaker 3D from another experiment)

I’m making a game in the spirit of Final Fantasy Tactics with mechs like those in Front Mission III. It looks like this after five days of coding and modeling (Those models aren’t the real ones, I’m not THAT cheap :stuck_out_tongue:):

Well, this are my questions:

-My camera controlling code looks like this:

camera.py:

from pandac.PandaModules import *
from direct.task import Task #Task Manager

class Camera: #Camera class, controls the point of view
    def __init__(self): #Start & setup the camera
        
        base.disableMouse() #Disable the default camera
        base.camLens.setNearFar(.2, 1000) #Set the clipping distance
                
        self.floater = NodePath(PandaNode("floater")) #Make a reference point
        self.floater.setPos( 0, 0, 0) #Set its position
        self.floater.setHpr(0, -30, 0) #and its orientation
        self.floater.reparentTo(render) #Parent it to Render
        
        camera.setPos(0, -30, 7) #Set the camera's position
        camera.setHpr(0, -15, 0) #and orientation
        camera.reparentTo(self.floater) #Parent it to the reference point
        taskMgr.add(self.cam_Update, "Cam_Update") #Add the camera update task
        
        self.center = None #Make a epicenter
        
        taskMgr.add(self.MouseInput, "MouseInput")  #Add the Mouse Input task
        taskMgr.add(self.cam_Update, "cam_Update")  #Add the Camera Update task
        
    def MouseInput(self, task): #Mouse Task
    
        if base.mouseWatcherNode.hasMouse(): #If the mouse is reachable
            mpos = base.mouseWatcherNode.getMouse() #Get mouse coordinates
                   
            if mpos.getX() > 0.8: #If the mouse is near the right edge of the screen
                if -0.7 < mpos.getY() < 0.7: #But not in the corners
                    self.cameraTurn(-5) #Make the camera rotate 5 degrees to the left
            elif mpos.getX() < -0.8: #If the mouse is near the left edge of the screen
                if -0.7 < mpos.getY() < 0.7: #But not in the corners
                    self.cameraTurn(5) #Make the camera rotate 5 degrees to the right
                
            if mpos.getY() > 0.8: #If the mouse is near the upper edge of the screen
                if -0.7 < mpos.getX() < 0.7: #But not in the corners
                    self.cameraPitch(-5) #Make the camera rotate 5 degrees up
            elif mpos.getY() < -0.8: #If the mouse is near the downer edge of the screen
                if -0.7 < mpos.getX() < 0.7: #But not in the corners
                    self.cameraPitch(5) #Make the camera rotate 5 degrees down
        
        return Task.cont #Loop It    
    
    def cameraTurn(self, dir): #Turn the camera
        self.floater.setH(self.floater, dir) #Change the reference point orientation

    def cameraZoom(self, dir): #Zoom the camera
        distvec = self.floater.getPos() - camera.getPos() #Calculate a vector in between the camera and the reference point
        print "Dist: %5.2f" % distvec.length()
        print "Dir: %2.2f" % dir
        
        if dir * 5 < distvec.length() < 100: #If the distance between them is valid
            self.floater.setZ(self.floater, dir / 5) #Lower the reference point
            camera.setY(camera, dir) #Move the camera
        elif dir <= dir * 5: #If the distance is smaller than 5 times the amount passed
            camera.setY(distvec.length()) #Stop the zoom
        elif dir >= 100: #If the distance is larger than 100
            camera.setY(distvec.length()) #Stop the zoom
        
    def cameraPitch(self,dir): #Pitch the camera
        if self.floater.getP() > -60: #If the pitch angle is bigger than -60 degrees
            if dir < 0: #and the passed amount is negative
                 self.floater.setP(self.floater, dir) #Set the reference point's pitch
        
        if self.floater.getP() < 0: #If the pitch angle is smaller than 0 degrees
            if dir > 0: #and the passed amount is positive
                 self.floater.setP(self.floater, dir) #Set the reference point's pitch
                 
    def cam_Update(self, Task): #Update camera task
        if self.center != None: #If there's a center
            self.floater.setPos(self.center.getX(), self.center.getY(), self.center.getZ() + 4) #Set the reference point's position to it's coordinates plus 4 in the Z axis
            base.camera.lookAt(self.floater) #Make the camera look at the reference point
        if self.floater.getR() <= 0: self.floater.setR(0) #Make a negative roll positive
        return Task.cont # Loop it

… The problems are two, one that when I have some pitch and I try to turn, my camera takes the pitch in consideration and after some turning I see the other side of the terrain, and the other that my zooming control isn’t working like it should, when I start the game the camera appears too close to the floater, when I zoom in the floater flips 180 degrees making the camera go the wrong way and if I zoom out totally and then try to zoom in it breaks and I end up seeing the terrain from above without any posibilities of zooming.

My second question is regarding my skysphere.

The code to set it is:

self.skymap = loader.loadModel("skymap")
        self.skymap.reparentTo(camera)
        self.skymap.setEffect(CompassEffect.make(render)) 
        self.skymap.setBin('background', 0) 
        self.skymap.setDepthWrite(False) 

and the sphere looks like this:

I tried calculating the normals inside (And in Blender, looking at the sphere showed me the other side of it, the texture showed too (Not very good because of the default light source)…
What can I do? :frowning:

And my last question is:

This is my random terrain generator:

def Generate_Terrain(self): #Generate Random Terrain
        self.heightmap = [] #Create an empty array
        for xc in range(size):
            self.heightmap.append([]) #Fill the array with empty arrays
            for yc in range(size):
                if random.random() > wet: #If a random number is greater than the wetness coeficient
                    if random.random() > hill: #If a random number is greater than the hillness coeficient
                        self.heightmap[xc].append(random.randint(1, max)) #Make the height in that place positive and smaller than the max
                    else: #Else
                        self.heightmap[xc].append(random.randint(1, 2)) #Make the height in that place 1 or 2 for flatter land
                else: #Else
                    self.heightmap[xc].append(random.randint(min, 0)) #Make the height in that place negative and bigger than the min

(wet (0 < x < 1) , hill (0 < x < 1) and size (2**x + 1) are consts defined in another file.

I’m not happy with the results, my terrains look like a sieve with no flat patches of terrain :frowning:
And yes, with that heightmap, later I make my tiles like this:

def Load_Terrain(self): #Create Terrain
        self.Generate_Terrain() #Generate a heightmap
        self.m_terrain = [] #Create an empty array
        for xc in range(size):
            self.m_terrain.append([]) #Fill the array with empty arrays
            for yc in range(size):
                if self.heightmap[xc][yc] > 0: #If the height is positive
                   self.m_terrain[xc].append(loader.loadModelCopy("T_Grass")) #Create a patch of grass
                   self.m_terrain[xc][yc].setPos(size * border - xc * border, size * border - yc * border,  0) #Set it to its position
                   self.m_terrain[xc][yc].setSz(self.heightmap[xc][yc] * 10) #Make it taller

                if self.heightmap[xc][yc] <= 0: #If the height is negative
                    self.m_terrain[xc].append(loader.loadModelCopy("T_Water")) #Create a pond
                    self.m_terrain[xc][yc].setPos(size * border - xc * border, size * border - yc * border,  -.1) #Set it to its position
                    self.m_terrain[xc][yc].setSz(1) #Make it thiner

                self.Cpicker.makePickable(self.m_terrain[xc][yc]) #Either way, make it pickable         
                self.m_terrain[xc][yc].reparentTo(render)#And reparent it to render

Sorry to bother you and I hope that you can help me (Whoever you might be),

Thanks a lot,

{} {} }{

PS: Yes, I comment my code line by line and in english :stuck_out_tongue:

With respect to your terrain, it looks like each cell height is calculated independently, right? This will lead to completely random terrain. What you want is fractal terrain, terrain that is random but follows natural contours.

Here is is some psuedocode for generating simple fractal terrain on an 8x8 grid (64 cells)

set all 64 heights in terrain[] array to zero

# set 4x4 base heights
for i in 0 to 3
    h = random(8)
    for j in i * 16 to i * 16 + 15
        terrain[j] += h

# add 2x2 medium offsets
for i in 0 to 16
    h = random(4)
    for j in i * 4 to i * 4 + 3
        terrain[j] += h

# add 1x1 small offsets
for i in 0 to 64
    h = random(2)
    terrain[j] += h

This algorithm starts by defining the terrain in large, coarse blocks, then adds progressive detail. You will also get better results if you do full resolution smoothing between each phase to avoid lots of sharp cliffs.

Then subtract your water height from every cell, and set cells with height <= 0 as water.

Google for “fractal terrain” if you’re interested in more sophisticated approaches, but most of these might be overkill for a small grid based game.

First, thank you very much for you answer and your time.

I had done yesterday some research in fractal terrains (Diamond-Square algorithm, mainly), but I got overwhelmed with the implementation…

The sharp cliffs are ok, I’m doing it for the nostalgia after all ^^

I think I’ll try your pseudocode :wink:

Regarding the camera, I think I have found a less conflicting way to accomplish what I wanted to do. It’s working almost perfectly now :smiley:

Now I’m working in a recursion function to draw paths for the characters, I hope it turns out ok :slight_smile:

I’ll try to give something to the community too, if anyone needs (Yeah, sure) a translation from/to english-spanish-german-portuguese-italian-french, PM me :slight_smile:

You can get an implementation of the diamond-square algorithm for Python/Panda here: discourse.panda3d.org/viewtopic.php?t=2431

Always search these forums! They are a gold mine of useful stuff.

Yes, I ended up doing the code myself from the same pseudocode that the snippet was made. Then I realized that for a small tile based game, fractals and the diamond squared algorithm was too much and the results simply weren’t as impressive, so I’m back with a height = randint(0, 2) function for the moment :stuck_out_tongue:

Last night I made the pathing algorithm and finished remaking the camera :slight_smile: