How can I load models and sounds into my distributable?

I have been trying to learn Panda3D through making a game through this Tutorial: Home | Panda3D Beginner’s Tutorial

I have managed to complete Lessons 1-15, and the game works fine through a command prompt; however, when I use bdist_apps to make it a distributable, the game crashes every time that I try to load it. When I go to the log, it gives me this error message:

Known pipe types: wglGraphicsPipe (all display modules loaded.)

:loader(error): Couldn’t load file PandaSampleModels-master/PandaSampleModels- master/Environment/environment.egg: not found on model path (currently: “/c/Users/hihos/Documents/Toontown Modding Project/Panda3D-1.10.9-x64/Projects/build/win_amd64;/…;/…/models”)

Traceback (most recent call last): File “main”, line 415, in File “main”, line 28, in init File “direct.showbase.Loader”, line 298, in loadModel OSError: Could not load model file(s): [‘PandaSampleModels-master/PandaSampleModels-master/Environment/environment’]

I am not very experienced in coding period, so I am not exactly sure how to fix a problem like this. I have tried to add the folder of the models and sounds to the requirements.txt file; however, it would not allow for me to build the product. I don’t know if the models, images, and sound files are already included in the .exe file of the game or if I need to add them through some other means.

Here is how my File Paths are like:

-Projects (Carries all of the main files for the game: Game.py, GameObject.py, requirements.txt, and setup.py)

-p3d_samples-master (Contains the character model used in the game.)

-PandaSampleModels-master (Contains the UI, environment, and enemies.)

-Sounds_&_Music (Contains the Sound Effects and Music.)

All of these are within my Panda3D-1.10.9-x64 folder. Any help would be greatly appreciated.

Here is my setup.py file:

from setuptools import setup

setup(
    name = "Panda-chan and the Endless Horde",
    options = {
        "build_apps" : {
            "include_patterns" : [
                "**/*.png",
                "**/*.ogg",
                "**/*.txt",
                "**/*.egg",
                "Fonts/*"
            ],
            "gui_apps" : {
                "Panda-chan and the Endless Horde" : "project1.py"
            },
            "plugins" : [
                "pandagl",
                "p3openal_audio"
            ],
            "platforms" : [
                "manylinux1_x86_64",
                "macosx_10_15_x86_64",
                "win_amd64"
            ],
            "log_filename" : "$USER_APPDATA/PandaChanAndHorde/output.log",
            "log_append" : False
            
        }
    }
)

Here is my main game file (project1.py):

from panda3d.core import Vec4, Vec3
from panda3d.core import CollisionTraverser
from panda3d.core import CollisionHandlerPusher
from panda3d.core import CollisionSphere, CollisionNode
from panda3d.core import CollisionTube
from direct.gui.DirectGui import *
from gameobject import *

import random

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.disableMouse()

        properties = WindowProperties()
        properties.setSize(1000, 750)
        self.win.requestProperties(properties)

        self.exitFunc = self.cleanup

        self.environment = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/Environment/environment")
        self.environment.reparentTo(render)

        self.camera.setPos(0, 0, 32)
        self.camera.setP(-90)

        ambientLight = AmbientLight("ambient light")
        ambientLight.setColor(Vec4(0.2, 0.2, 0.2, 1))
        self.ambientLightNodePath = render.attachNewNode(ambientLight)
        render.setLight(self.ambientLightNodePath)

        mainLight = DirectionalLight("main light")
        self.mainLightNodePath = render.attachNewNode(mainLight)
        self.mainLightNodePath.setHpr(45, -45, 0)
        render.setLight(self.mainLightNodePath)

        render.setShaderAuto()

        self.pusher = CollisionHandlerPusher()
        self.cTrav = CollisionTraverser()

        self.pusher.setHorizontal(True)

        self.pusher.add_in_pattern("%fn-into-%in")
        self.accept("trapEnemy-into-wall", self.stopTrap)
        self.accept("trapEnemy-into-trapEnemy", self.stopTrap)
        self.accept("trapEnemy-into-player", self.trapHitsSomething)
        self.accept("trapEnemy-into-walkingEnemy", self.trapHitsSomething)
        
        wallSolid = CollisionTube(-8.0, 0, 0, 8.0, 0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setY(8.0)

        wallSolid = CollisionTube(-8.0, 0, 0, 8.0, 0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setY(-8.0)

        wallSolid = CollisionTube(0, -8.0, 0, 0, 8.0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setX(8.0)

        wallSolid = CollisionTube(0, -8.0, 0, 0, 8.0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setX(-8.0)

        self.keyMap = {
            "up" : False,
            "down" : False,
            "left" : False,
            "right" : False,
            "shoot" : False
        }

        self.accept("w", self.updateKeyMap, ["up", True])
        self.accept("w-up", self.updateKeyMap, ["up", False])
        self.accept("s", self.updateKeyMap, ["down", True])
        self.accept("s-up", self.updateKeyMap, ["down", False])
        self.accept("a", self.updateKeyMap, ["left", True])
        self.accept("a-up", self.updateKeyMap, ["left", False])
        self.accept("d", self.updateKeyMap, ["right", True])
        self.accept("d-up", self.updateKeyMap, ["right", False])
        self.accept("mouse1", self.updateKeyMap, ["shoot", True])
        self.accept("mouse1-up", self.updateKeyMap, ["shoot", False])

        self.updateTask = taskMgr.add(self.update, "update")

        self.player = None

        self.enemies = []
        self.trapEnemies = []
        self.deadEnemies = []

        self.spawnPoints = []
        numPointsPerWall = 5
        for i in range(numPointsPerWall):
            coord = 7.0/numPointsPerWall + 0.5
            self.spawnPoints.append(Vec3(-7.0, coord, 0))
            self.spawnPoints.append(Vec3(7.0, coord, 0))
            self.spawnPoints.append(Vec3(coord, -7.0, 0))
            self.spawnPoints.append(Vec3(coord, 7.0, 0))

        self.initialSpawnInterval = 1.0
        self.minimumSpawnInterval = 0.2
        self.spawnInterval = self.initialSpawnInterval
        self.spawnTimer = self.spawnInterval
        self.maxEnemies = 2
        self.maximumMaxEnemies = 20

        self.numTrapsPerSide = 2

        self.difficultyInterval = 5.0
        self.difficultyTimer = self.difficultyInterval

        music = loader.loadMusic("Sounds_&_Music/Defending-the-Princess-Haunted_v002.ogg")
        music.setLoop(True)
        music.setVolume(0.075)
        music.play()

        self.enemySpawnSound = loader.loadSfx("Sounds_&_Music/Sounds_enemySpawn.ogg")

        self.gameOverScreen = DirectDialog(frameSize= (-0.7, 0.7, -0.7, 0.7),
                                           fadeScreen = 0.4,
                                           relief = DGG.FLAT,
                                           frameTexture = "PandaSampleModels-master/PandaSampleModels-master/UI/stoneFrame.png")
        self.gameOverScreen.hide()

        self.font = loader.loadFont("wbx_komik/Wbxkomik.ttf")

        label = DirectLabel(text = "Game Over!",
                            parent = self.gameOverScreen,
                            scale = 0.1,
                            pos = (0, 0, 0.2),
                            text_font = self.font,
                            relief = None)

        self.finalScoreLabel = DirectLabel(text = "",
                                           parent = self.gameOverScreen,
                                           scale = 0.07,
                                           pos = (0, 0, 0),
                                           text_font = self.font,
                                           relief = None)

        buttonImages = (
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButton.png"),
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButtonPressed.png"),
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButtonHighlighted.png"),
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButtonDisabled.png")
        )

        btn = DirectButton(text = "Restart",
                           command = self.startGame,
                           pos = (-0.3, 0, -0.2),
                           parent = self.gameOverScreen,
                           scale = 0.07,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        
        btn.setTransparency(True)
    

        btn = DirectButton(text = "Quit",
                           command = self.quit,
                           pos = (0.3, 0, -0.2),
                           parent = self.gameOverScreen,
                           scale = 0.07,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        
        btn.setTransparency(True)

        self.titleMenuBackdrop = DirectFrame(frameColor = (0, 0, 0, 1),
                                             frameSize = (-1, 1, -1, 1),
                                             parent = render2d)
        self.titleMenu = DirectFrame(frameColor = (1, 1, 1, 0))

        title = DirectLabel(text = "Panda-chan",
                            scale = 0.1,
                            pos = (0, 0, 0.9),
                            parent = self.titleMenu,
                            relief = None,
                            text_font = self.font,
                            text_fg = (1, 1, 1, 1))
        title2 = DirectLabel(text = "and the",
                             scale = 0.07,
                             pos = (0, 0, 0.79),
                             parent = self.titleMenu,
                             text_font = self.font,
                             frameColor = (0.5, 0.5, 0.5, 1))
        title3 = DirectLabel(text = "Endless Horde",
                              scale = 0.125,
                              pos = (0, 0, 0.65),
                              parent = self.titleMenu,
                              relief = None,
                              text_font = self.font,
                              text_fg = (1, 1, 1, 1))
        btn = DirectButton(text = "Start Game",
                           command = self.startGame,
                           pos = (0, 0, 0.2),
                           parent = self.titleMenu,
                           scale = 0.1,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        btn.setTransparency(True)

        btn = DirectButton(text = "Quit",
                           command = self.quit,
                           pos = (0, 0, -0.2),
                           parent = self.titleMenu,
                           scale = 0.1,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        btn.setTransparency(True)
        
    def startGame(self):
        self.titleMenu.hide()
        self.titleMenuBackdrop.hide()
        self.gameOverScreen.hide()
        

        self.cleanup()

        self.player = Player()

        self.maxEnemies = 2
        self.spawnInterval = self.initialSpawnInterval

        self.difficultyTimer = self.difficultyInterval

        sideTrapSlots = [
            [],
            [],
            [],
            []
        ]
        trapSlotDistance = 0.4
        slotPos = -8 + trapSlotDistance
        while slotPos < 8:
            if abs(slotPos) > 1.0:
                sideTrapSlots[0].append(slotPos)
                sideTrapSlots[1].append(slotPos)
                sideTrapSlots[2].append(slotPos)
                sideTrapSlots[3].append(slotPos)
            slotPos += trapSlotDistance

        for i in range(self.numTrapsPerSide):
            slot = sideTrapSlots[0].pop(random.randint(0, len(sideTrapSlots[0])-1))
            trap = TrapEnemy(Vec3(slot, -7.0, 0))
            self.trapEnemies.append(trap)

            slot = sideTrapSlots[1].pop(random.randint(0, len(sideTrapSlots[1])-1))
            trap = TrapEnemy(Vec3(slot, -7.0, 0))
            self.trapEnemies.append(trap)

            slot = sideTrapSlots[2].pop(random.randint(0, len(sideTrapSlots[2])-1))
            trap = TrapEnemy(Vec3(7.0, slot, 0))
            trap.moveInX = True
            self.trapEnemies.append(trap)

            slot = sideTrapSlots[3].pop(random.randint(0, len(sideTrapSlots[3])-1))
            trap = TrapEnemy(Vec3(-7.0, slot, 0))
            trap.moveInX = True
            self.trapEnemies.append(trap)


    def updateKeyMap(self, controlName, controlState):
        self.keyMap[controlName] = controlState

    def stopTrap(self, entry):
        collider = entry.getFromNodePath()
        if collider.hasPythonTag("owner"):
            trap = collider.getPythonTag("owner")
            trap.moveDirection = 0
            trap.ignorePlayer = False
            trap.movementSound.stop()
            trap.stopSound.play()
            
    def trapHitsSomething(self, entry):
        collider = entry.getFromNodePath()
        if collider.hasPythonTag("owner"):
            trap = collider.getPythonTag("owner")
            if trap.moveDirection == 0:
                return
            collider = entry.getIntoNodePath()
            if collider.hasPythonTag("owner"):
                obj = collider.getPythonTag("owner")
                if isinstance(obj, Player):
                    if not trap.ignorePlayer:
                        obj.alterHealth(-1)
                        trap.ignorePlayer = True
                else:
                    obj.alterHealth(-10)
                trap.impactSound.play()
                    

    def spawnEnemy(self):
        if len(self.enemies) < self.maxEnemies:
            spawnPoint = random.choice(self.spawnPoints)

            newEnemy = WalkingEnemy(spawnPoint)

            self.enemies.append(newEnemy)

            self.enemySpawnSound.play()

    def update(self, task):

        dt = globalClock.getDt()

        if self.player is not None:
                if self.player.health > 0:
                    self.player.update(self.keyMap, dt)

                    self.spawnTimer -= dt
                    if self.spawnTimer <= 0:
                        self.spawnTimer = self.spawnInterval
                        self.spawnEnemy()

                    [enemy.update(self.player, dt) for enemy in self.enemies]
                    [trap.update(self.player, dt) for trap in self.trapEnemies]

                    newlyDeadEnemies = [enemy for enemy in self.enemies if enemy.health <= 0]
                    self.enemies = [enemy for enemy in self.enemies if enemy.health > 0]

                    for enemy in newlyDeadEnemies:
                        enemy.collider.removeNode()
                        enemy.actor.play("die")
                        self.player.score += enemy.scoreValue
                    if len(newlyDeadEnemies) > 0:
                        self.player.updateScore()

                    self.deadEnemies += newlyDeadEnemies

                    enemiesAnimatingDeaths = []
                    for enemy in self.deadEnemies:
                        deathAnimControl = enemy.actor.getAnimControl("die")
                        if deathAnimControl is None or not deathAnimControl.isPlaying():
                            enemy.cleanup()
                        else:
                            enemiesAnimatingDeaths.append(enemy)
                    self.deadEnemies = enemiesAnimatingDeaths

                    self.difficultyTimer -= dt
                    if self.difficultyTimer <= 0:
                        self.difficultyTimer = self.difficultyInterval
                        if self.maxEnemies < self.maximumMaxEnemies:
                            self.maxEnemies += 1
                        if self.spawnInterval > self.minimumSpawnInterval:
                            self.spawnInterval -= 0.1

                else:
                    if self.gameOverScreen.isHidden():
                        self.gameOverScreen.show()
                        self.finalScoreLabel["text"] = "Final score: " + str(self.player.score)
                        self.finalScoreLabel.setText()
                        
        return task.cont
    
    def cleanup(self):
        for enemy in self.enemies:
            enemy.cleanup()
        self.enemies = []

        for enemy in self.deadEnemies:
            enemy.cleanup()
        self.deadEnemies = []

        for trap in self.trapEnemies:
            trap.cleanup()
        self.trapEnemies = []

        if self.player is not None:
            self.player.cleanup()
            self.player = None

    def quit(self):
        self.cleanup()

        base.userExit()

game = Game()
game.run()


And here is my gameobject.py file (Holds all of the characters):

from panda3d.core import Vec3, Vec2, Plane, Point3, BitMask32, Vec4
from direct.actor.Actor import Actor
from panda3d.core import CollisionSphere, CollisionNode, CollisionRay, CollisionHandlerQueue, CollisionSegment
from direct.gui.OnscreenText import OnscreenText
from direct.gui.OnscreenImage import OnscreenImage
from panda3d.core import TextNode
from panda3d.core import PointLight
from panda3d.core import AudioSound

import math
import random

FRICTION = 150.0

class GameObject():
    def __init__(self, pos, modelName, modelAnims, maxHealth, maxSpeed, colliderName):
        self.actor = Actor(modelName, modelAnims)
        self.actor.reparentTo(render)
        self.actor.setPos(pos)

        self.maxHealth = maxHealth
        self.health = maxHealth

        self.maxSpeed = maxSpeed

        self.velocity = Vec3(0, 0, 0)
        self.acceleration = 300.0

        self.walking = False

        colliderNode = CollisionNode(colliderName)
        colliderNode.addSolid(CollisionSphere(0, 0, 0, 0.3))
        self.collider = self.actor.attachNewNode(colliderNode)
        self.collider.setPythonTag("owner", self)

        self.deathSound = None

    def update(self, dt):
        speed = self.velocity.length()
        if speed > self.maxSpeed:
            self.velocity.normalize()
            self.velocity *= self.maxSpeed
            speed = self.maxSpeed

        if not self.walking:
            frictionVal = FRICTION*dt
            if frictionVal > speed:
                self.velocity.set(0, 0, 0)
            else:
                frictionVec = -self.velocity
                frictionVec.normalize()
                frictionVec *= frictionVal

                self.velocity += frictionVec

        self.actor.setPos(self.actor.getPos() + self.velocity*dt)

    def alterHealth(self, dHealth):
        previousHealth = self.health
        
        self.health += dHealth

        if self.health > self.maxHealth:
            self.health = self.maxHealth

        if previousHealth> 0 and self.health <= 0 and self.deathSound is not None:
            self.deathSound.play()

    def cleanup(self):
        if self.collider is not None and not self.collider.isEmpty():
            self.collider.clearPythonTag("owner")
            base.cTrav.removeCollider(self.collider)
            base.pusher.removeCollider(self.collider)

        if self.actor is not None:
            self.actor.cleanup()
            self.actor.removeNode()
            self.actor = None

        self.collider = None




class Player(GameObject):
    def __init__(self):
        GameObject.__init__(self,
                            Vec3(0, 0, 0),
                            "p3d_samples-master/models/act_p3d_chan",
                                {
                                  "stand" : "p3d_samples-master/models/a_p3d_chan_idle",
                                  "walk" : "p3d_samples-master/models/a_p3d_chan_run"
                                },
                            5,
                            10,
                            "player")
        self.actor.getChild(0).setH(180)

        mask = BitMask32()
        mask.setBit(1)

        self.collider.node().setIntoCollideMask(mask)

        mask = BitMask32()
        mask.setBit(1)

        self.collider.node().setFromCollideMask(mask)
        
        base.pusher.addCollider(self.collider, self.actor)
        base.cTrav.addCollider(self.collider, base.pusher)

        self.lastMousePos = Vec2(0, 0)

        self.groundPlane = Plane(Vec3(0, 0, 1), Vec3(0, 0, 0))

        self.ray = CollisionRay(0, 0, 0, 0, 1, 0)

        rayNode = CollisionNode("playerRay")
        rayNode.addSolid(self.ray)

        mask = BitMask32()

        mask.setBit(2)
        rayNode.setFromCollideMask(mask)

        mask = BitMask32()
        rayNode.setIntoCollideMask(mask)

        self.rayNodePath = render.attachNewNode(rayNode)
        self.rayQueue = CollisionHandlerQueue()

        base.cTrav.addCollider(self.rayNodePath, self.rayQueue)

        self.damagePerSecond = -5.0

        self.beamModel = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/BambooLaser/bambooLaser")
        self.beamModel.reparentTo(self.actor)
        self.beamModel.setZ(1.5)
        self.beamModel.setLightOff()
        self.beamModel.hide()

        self.beamHitModel = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/BambooLaser/bambooLaserHit")
        self.beamHitModel.reparentTo(render)
        self.beamHitModel.setZ(1.5)
        self.beamHitModel.setLightOff()
        self.beamHitModel.hide()

        self.beamHitPulseRate = 0.15
        self.beamHitTimer = 0

        self.beamHitLight = PointLight("beamHitLight")
        self.beamHitLight.setColor(Vec4(0.1, 1.0, 0.2, 1))
        self.beamHitLight.setAttenuation((1.0, 0.1, 0.5))
        self.beamHitLightNodePath = render.attachNewNode(self.beamHitLight)

        self.lastMousePos = Vec2(0, 0)

        self.groundPlane = Plane(Vec3(0, 0, 1), Vec3(0, 0, 0))

        self.yVector = Vec2(0, 1)

        self.actor.loop("stand")

        self.score = 0

        self.scoreUI = OnscreenText(text = "0",
                                    pos = (-1.3, 0.825),
                                    mayChange = True,
                                    align = TextNode.ALeft)

        self.healthIcons = []
        for i in range(self.maxHealth):
            icon = OnscreenImage(image = "PandaSampleModels-master/PandaSampleModels-master/UI/health.png",
                                 pos = (-1.275 + i*0.075, 0, 0.95),
                                 scale = 0.04)
            icon.setTransparency(True)
            self.healthIcons.append(icon)

        self.damageTakenModel = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/BambooLaser/playerHit")
        self.damageTakenModel.setLightOff()
        self.damageTakenModel.setZ(1.0)
        self.damageTakenModel.reparentTo(self.actor)
        self.damageTakenModel.hide()

        self.damageTakenModelTimer = 0
        self.damageTakenModelDuration = 0.15

        self.laserSoundNoHit = loader.loadSfx("Sounds_&_Music/Sounds_laserNoHit.ogg")
        self.laserSoundNoHit.setLoop(True)
        self.laserSoundHit = loader.loadSfx("Sounds_&_Music/Sounds_laserHit.ogg")
        self.laserSoundHit.setLoop(True)

        self.hurtSound = loader.loadSfx("Sounds_&_Music/Sounds_FemaleDmgNoise.ogg")

        self.scoreUI = OnscreenText(text = "0",
                                    pos = (-1.3, 0.825),
                                    mayChange = True,
                                    align = TextNode.ALeft,
                                    font = base.font)
    def update(self, keys, dt):
        GameObject.update(self, dt)

        self.walking = False

        if keys["up"]:
            self.walking = True
            self.velocity.addY(self.acceleration*dt)
        if keys["down"]:
            self.walking = True
            self.velocity.addY(-self.acceleration*dt)
        if keys["left"]:
            self.walking = True
            self.velocity.addX(-self.acceleration*dt)
        if keys["right"]:
            self.walking = True
            self.velocity.addX(self.acceleration*dt)

        if self.walking:
            standControl = self.actor.getAnimControl("stand")
            if standControl.isPlaying():
                standControl.stop()

            walkControl = self.actor.getAnimControl("walk")
            if not walkControl.isPlaying():
                self.actor.loop("walk")
        else:
            standControl = self.actor.getAnimControl("stand")
            if not standControl.isPlaying():
                self.actor.stop("walk")
                self.actor.loop("stand")

        if keys["shoot"]:
            if self.rayQueue.getNumEntries() > 0:
                scoredHit = False
                
                self.rayQueue.sortEntries()
                rayHit = self.rayQueue.getEntry(0)
                hitPos = rayHit.getSurfacePoint(render)

                hitNodePath = rayHit.getIntoNodePath()
                print (hitNodePath)
                if hitNodePath.hasPythonTag("owner"):
                    hitObject = hitNodePath.getPythonTag("owner")
                    if not isinstance(hitObject, TrapEnemy):
                        hitObject.alterHealth(self.damagePerSecond*dt)
                        scoredHit = True

                beamLength = (hitPos - self.actor.getPos()).length()
                self.beamModel.setSy(beamLength)

                self.beamModel.show()

                if scoredHit:
                    if self.laserSoundNoHit.status() == AudioSound.PLAYING:
                        self.laserSoundNoHit.stop()
                    if self.laserSoundHit.status() != AudioSound.PLAYING:
                        self.laserSoundHit.play()
                        
                    self.beamHitModel.show()

                    self.beamHitModel.setPos(hitPos)
                    self.beamHitLightNodePath.setPos(hitPos + Vec3(0, 0, 0.5))

                    if not render.hasLight(self.beamHitLightNodePath):
                        render.setLight(self.beamHitLightNodePath)

                else:
                    if self.laserSoundHit.status() == AudioSound.PLAYING:
                        self.laserSoundHit.stop()
                    if self.laserSoundNoHit.status() != AudioSound.PLAYING:
                        self.laserSoundNoHit.play()
                        
                    if render.hasLight(self.beamHitLightNodePath):
                        render.clearLight(self.beamHitLightNodePath)

                    self.beamHitModel.hide()
        else:
            if self.laserSoundNoHit.status() == AudioSound.PLAYING:
                self.laserSoundNoHit.stop()
            if self.laserSoundHit.status() == AudioSound.PLAYING:
                self.laserSoundHit.stop()
                
            if render.hasLight(self.beamHitLightNodePath):
                render.clearLight(self.beamHitLightNodePath)
                
            self.beamModel.hide()
            self.beamHitModel.hide()

        mouseWatcher = base.mouseWatcherNode
        if mouseWatcher.hasMouse():
            mousePos = mouseWatcher.getMouse()
        else:
            mousePos = self.lastMousePos

        mousePos3D = Point3()
        nearPoint = Point3()
        farPoint = Point3()

        base.camLens.extrude(mousePos, nearPoint, farPoint)

        self.groundPlane.intersectsLine(mousePos3D,
                                        render.getRelativePoint(base.camera, nearPoint),
                                        render.getRelativePoint(base.camera, farPoint))

        firingVector = Vec3(mousePos3D - self.actor.getPos())
        firingVector2D = firingVector.getXy()
        firingVector2D.normalize()
        firingVector.normalize()

        heading = self.yVector.signedAngleDeg(firingVector2D)

        self.actor.setH(heading)

        if firingVector.length() > 0.001:
            self.ray.setOrigin(self.actor.getPos())
            self.ray.setDirection(firingVector)

        self.lastMousePos = mousePos

        self.beamHitTimer -= dt
        if self.beamHitTimer <= 0:
            self.beamHitTimer = self.beamHitPulseRate
            self.beamHitModel.setH(random.uniform(0.0, 360.0))
        self.beamHitModel.setScale(math.sin(self.beamHitTimer*3.142/self.beamHitPulseRate)*0.4 + 0.9)
        
        if self.damageTakenModelTimer > 0:
            self.damageTakenModelTimer -= dt
            self.damageTakenModel.setScale(2.0 - self.damageTakenModelTimer/self.damageTakenModelDuration)
            if self.damageTakenModelTimer <= 0:
                self.damageTakenModel.hide()
                
    def updateScore(self):
        self.scoreUI.setText(str(self.score))

    def alterHealth(self, dHealth):
        GameObject.alterHealth(self, dHealth)

        self.updateHealthUI()

        self.damageTakenModel.show()
        self.damageTakenModel.setH(random.uniform(0.0, 360.0))
        self.damageTakenModelTimer = self.damageTakenModelDuration

        self.hurtSound.play()

    def updateHealthUI(self):
        for index, icon in enumerate(self.healthIcons):
            if index < self.health:
                icon.show()
            else:
                icon.hide()
                
    def cleanup(self):
        base.cTrav.removeCollider(self.rayNodePath)

        self.scoreUI.removeNode()

        for icon in self.healthIcons:
            icon.removeNode()

        self.beamHitModel.removeNode()

        render.clearLight(self.beamHitLightNodePath)
        self.beamHitLightNodePath.removeNode()

        GameObject.cleanup(self)

        self.laserSoundHit.stop()
        self.laserSoundNoHit.stop()
                                                        
class Enemy(GameObject):
    def __init__(self, pos, modelName, modelAnims, maxHealth, maxSpeed, colliderName):
        GameObject.__init__(self, pos, modelName, modelAnims, maxHealth, maxSpeed, colliderName)

        self.scoreValue = 1

    def update(self, player, dt):
        GameObject.update(self, dt)
        self.runLogic(player, dt)

        if self.walking:
            walkingControl = self.actor.getAnimControl("walk")
            if not walkingControl.isPlaying():
                self.actor.loop("walk")
        else:
            spawnControl = self.actor.getAnimControl("spawn")
            if spawnControl is None or not spawnControl.isPlaying():
                attackControl = self.actor.getAnimControl("attack")
                if attackControl is None or not attackControl.isPlaying():
                    standControl = self.actor.getAnimControl("stand")
                    if not standControl.isPlaying():
                        self.actor.loop("stand")

    def runLogic(self, player, dt):
        pass

class WalkingEnemy(Enemy):
    def __init__(self, pos):
        Enemy.__init__(self, pos,
                       "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy",
                       {
                        "stand" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-stand",
                        "walk" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-walk",
                        "attack" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-attack",
                        "die" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-die",
                        "spawn" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-spawn"
                        },
                       3.0,
                       7.0,
                       "walkingEnemy")

        self.attackDistance = 0.75
        self.acceleration = 100.0

        self.yVector = Vec2(0, 1)

        mask = BitMask32()
        mask.setBit(2)

        self.collider.node().setIntoCollideMask(mask)

        self.attackSegment = CollisionSegment(0, 0, 0, 1, 0, 0)

        segmentNode = CollisionNode("enemyAttackSegment")
        segmentNode.addSolid(self.attackSegment)

        mask = BitMask32()
        mask.setBit(1)

        segmentNode.setFromCollideMask(mask)

        mask = BitMask32()

        segmentNode.setIntoCollideMask(mask)

        self.attackSegmentNodePath = render.attachNewNode(segmentNode)
        self.segmentQueue = CollisionHandlerQueue()

        base.cTrav.addCollider(self.attackSegmentNodePath, self.segmentQueue)

        self.attackDamage = -1

        self.attackDelay = 0.3
        self.attackDelayTimer = 0
        self.attackWaitTimer = 0

        self.actor.play("spawn")

        self.deathSound = loader.loadSfx("Sounds_&_Music/Sounds_enemyDie.ogg")
        self.attackSound = loader.loadSfx("Sounds_&_Music/Sounds_enemyAttack.ogg")
    def runLogic(self, player, dt):
        spawnControl = self.actor.getAnimControl("spawn")
        if spawnControl is not None and spawnControl.isPlaying():
            return
        
        vectorToPlayer = player.actor.getPos() - self.actor.getPos()

        vectorToPlayer2D = vectorToPlayer.getXy()
        distanceToPlayer = vectorToPlayer2D.length()

        vectorToPlayer2D.normalize()

        heading = self.yVector.signedAngleDeg(vectorToPlayer2D)

        if distanceToPlayer > self.attackDistance*0.9:
            self.walking = True
            vectorToPlayer.setZ(0)
            vectorToPlayer.normalize()
            self.velocity += vectorToPlayer*self.acceleration*dt
            self.attackWaitTimer = 0.2
            self.attackDelayTimer = 0
        else:
            self.walking = False
            self.velocity.set(0, 0, 0)

            if self.attackDelayTimer > 0:
                self.attackDelayTimer -= dt
                if self.attackDelayTimer <= 0:
                    if self.segmentQueue.getNumEntries() > 0:
                        self.segmentQueue.sortEntries()
                        segmentHit = self.segmentQueue.getEntry(0)

                        hitNodePath = segmentHit.getIntoNodePath()
                        if hitNodePath.hasPythonTag("owner"):
                            hitObject = hitNodePath.getPythonTag("owner")
                            hitObject.alterHealth(self.attackDamage)
                            self.attackWaitTimer = 1.0
            elif self.attackWaitTimer > 0:
                self.attackWaitTimer -= dt
                if self.attackWaitTimer <= 0:
                    self.attackWaitTimer = random.uniform(0.5, 0.7)
                    self.attackDelayTimer = self.attackDelay
                    self.actor.play("attack")
                    self.attackSound.play()

        self.actor.setH(heading)

        self.attackSegment.setPointA(self.actor.getPos())
        self.attackSegment.setPointB(self.actor.getPos() + self.actor.getQuat().getForward()*self.attackDistance)

    def alterHealth(self, dHealth):
        Enemy.alterHealth(self, dHealth)
        self.updateHealthVisual()

    def updateHealthVisual(self):
        perc = self.health/self.maxHealth
        if perc < 0:
            perc = 0

        self.actor.setColorScale(perc, perc, perc, 1)
        
    def cleanup(self):
        base.cTrav.removeCollider(self.attackSegmentNodePath)
        self.attackSegmentNodePath.removeNode()

        GameObject.cleanup(self)
class TrapEnemy(Enemy):
    def __init__(self, pos):
        Enemy.__init__(self, pos,
                       "PandaSampleModels-master/PandaSampleModels-master/SlidingTrap/trap",
                       {
                        "stand" : "PandaSampleModels-master/PandaSampleModels-master/SlidingTrap/trap-stand",
                        "walk" : "PandaSampleModels-master/PandaSampleModels-master/SlidingTrap/trap-walk",
                        },
                       100.0,
                       10.0,
                       "trapEnemy")
        base.pusher.addCollider(self.collider, self.actor)
        base.cTrav.addCollider(self.collider, base.pusher)

        self.moveInX = False
        self.moveDirection = 0
        self.ignorePlayer = False

        mask = BitMask32()
        mask.setBit(2)
        mask.setBit(1)

        self.collider.node().setIntoCollideMask(mask)

        mask = BitMask32()
        mask.setBit(2)
        mask.setBit(1)

        self.collider.node().setFromCollideMask(mask)

        self.impactSound = loader.loadSfx("Sounds_&_Music/Sounds_trapHitsSomething.ogg")
        self.stopSound = loader.loadSfx("Sounds_&_Music/Sounds_trapStop.ogg")
        self.movementSound = loader.loadSfx("Sounds_&_Music/Sounds_trapSlide.ogg")
        self.movementSound.setLoop(True)

    def runLogic(self, player, dt):
        if self.moveDirection != 0:
            self.walking = True
            if self.moveInX:
                self.velocity.addX(self.moveDirection*self.acceleration*dt)
            else:
                self.velocity.addY(self.moveDirection*self.acceleration*dt)
        else:
            self.walking = False
            diff = player.actor.getPos() - self.actor.getPos()
            if self.moveInX:
                detector = diff.y
                movement = diff.x
            else:
                detector = diff.x
                movement = diff.y

            if abs(detector) < 0.5:
                self.moveDirection = math.copysign(1, movement)
                self.movementSound.play()

    def alterHealth(self, dHealth):
        pass

    def cleanup(self):
        self.movementSound.stop()

        Enemy.cleanup(self)
                       

Welcome to the forum! :slight_smile:

When you open up the directory that contains your build, is the environment model present? (Whether in that directory itself or, more likely, in a sub-directory.) If so, what is the directory-path that contains it, precisely? (Starting with the “build” folder, perhaps.)

Indeed, the “requirements.txt” file doesn’t handle issues like this, I believe. Rather, this is likely to be solved either in the “setup.py” file, or with some change to your directory structure, I think.

No, it is not in the build. How exactly would I go about doing such? Would I just drag the files into the build file? Would I need to convert the files into BAM files? Would I need to change the File Path in my coding?

Odd! It should be included into the “build” directory automatically, I do believe!

The fact that it isn’t suggests that there’s something that the build system dislikes about your particular arrangement of files, or something similar…

All right… You listed your file-paths, but would you please show a screenshot of the relevant directory-hierarchy? The folder in which you’re working on the game, to be specific, not your “build” folder. Perhaps open the folder in your file-manager, then in the folder-tree open each sub-folder and each sub-folder of those, and so on.

I’m hoping that the structure there will provide some clue as to what’s going wrong here…

OK, here are the different screenshots:

First, here is where all of the main files are stored:

Second, here is the “Projects” folder, which is where the main files are stored:

Third, here is the folder containing the model and animations for the player-character:

Fourth, here are the files containing the Environment, enemy models, attack models, and the UI:

Aaah, I thought that it might be so–I think that I see the problem!

In short, you have your models, etc. in folders on the same level as your main project-folder–in sibling folders, that is. The setup script is written under the assumption that these things are instead kept in sub-folders–i.e. child-folders (as the tutorial has them)

Hence the “include_patterns” file-extension values all starting with “**/*”, which I think indicates “any file-path at the same level as or below the current one”–“the current one” being the folder in which “setup.py” is found. (Or from which “setup.py” is run; I’m not sure.)

You might be able to tell the build-system to look in sibling-folders by changing that first part of the file-extension values in “include_patterns”. However, I’m honestly not sure of what the result will be in your “build”-directory, or how it might affect packaging.

OK, that makes sense. Do you think I may be able to copy and paste those folders into the same folder as setup.py to fix the problem?

I would think so, although it may call for adjusting your calls to “loadModel” to match.

Alrighty. I’m going to try it and hope it works. Thanks for your help!

1 Like

Not a problem! I hope that you get it working! :slight_smile:

Had to solve some other problems, but it’s finally up and running! Thanks again!

2 Likes

Excellent! I’m glad that you got it up and running! And I’m glad to have been of service! :slight_smile: