title: How to make camera movement in a pygame 3d engine from scratch?
body: <p>I am new to matrices and matrix transformations and other things. I am making a 3d engine of my own in pygame using 2d, but I ran into a problem. I can't get my engine to implement camera movement and it only does camera rotation. Here is my code:</p>
  <p>window.py: The custom window class</p>
  <pre><code>from .general_imports import *
  
  class Py3dWindow:
      def __init__(self, title=None, width=800, height=600,):
          self.title = title
          self.window = None
          self.width = width
          self.height = height
          self.meshes = []
          self.events_assinged = False
      
          self.projection_matrix = np.matrix([
              [1, 0, 0, 0],
              [0, 1, 0, 0],
              [0, 0, 1, 0],
              [0, 0, 0, 1]
          ])
          print(self.projection_matrix)
  
          self.camera = [
              [0, 0, 0, 0],
              [0, 0, 0],
              75, # fov
              1, # near
              1000 # far
          ]
  
      def add_mesh(self, mesh):
          self.meshes.append(mesh)
  
      def translate_matrix(self):
          angle_x = math.radians(self.camera[1][0])
          angle_y = math.radians(self.camera[1][1])
          angle_z = math.radians(self.camera[1][2])
  
          pos_x = self.camera[0][0]
          pos_y = self.camera[0][1]
          pos_z = self.camera[0][2]
  
          rotation_z = np.matrix([
              [int(np.cos(angle_z)), -int(np.sin(angle_z)), 0, 0],
              [int(np.sin(angle_z)), int(np.cos(angle_z)), 0, 0],
              [0, 0, 1, 0],
              [0, 0, 0, 1]
          ],)
  
          rotation_y = np.matrix([
              [int(np.cos(angle_y)), 0, int(np.sin(angle_y))],
              [0, 1, 0, 0],
              [-int(np.sin(angle_y)), 0, int(np.cos(angle_y))],
              [0, 0, 0, 1]
          ])
  
          rotation_x = np.matrix([
              [1, 0, 0, 0],
              [0, int(np.cos(angle_x)), -int(np.sin(angle_x)), 0],
              [0, int(np.sin(angle_x)), int(np.cos(angle_x)), 0],
              [0, 0, 0, 1],
          ])
  
          position_matrix = np.matrix([
              [1, 0, 0, pos_x],
              [0, 1, 0, pos_y],
              [0, 0, 1, pos_z],
              [0, 0, 0, 1],
          ])
          
          self.projection_matrix = np.dot(rotation_z, np.dot(rotation_y, rotation_x)) * position_matrix
  
          print(self.projection_matrix)
  
      def init_window(self):
          pygame.init()
          self.window = pygame.display.set_mode((self.width, self.height), pygame.RESIZABLE)
          pygame.display.set_caption(self.title)
          self.running = False
  
      def run(self,):
          self.running = True
          while self.running:
              self.draw()
              self.events(pygame.event.get())
              self.update()
              self.translate_matrix()
          pygame.quit()
  
      def events(self, events):
          for event in events:
              if event.type == pygame.QUIT:
                  self.running = False
          if self.events_assinged:
              self._events(events)
  
      def update(self,):
          for event in pygame.event.get():
              if event.type == pygame.QUIT:
                  self.running = False
  
      def draw(self,):
          self.window.fill((0, 0, 0))
          for mesh in self.meshes:
              mesh.draw(self.window)
          pygame.display.update()
          time.sleep(0.001)
  
      def on_events(self, func):
          self._events = func
          self.events_assinged = True
      
      def on_update(self, func):
          self.update = lambda: func()
  
      def on_draw(self, func):
          self.draw = lambda: func()
  
  </code></pre>
  <p>mesh.py: Mesh class</p>
  <pre><code>from .general_imports import *
  
  class Mesh:
      def __init__(self, parnet):
          self.points = []
          self.parent = parnet
  
      def add_point(self, position):
          position = [position[0] * 100, position[1] * 100, position[2] * 100, 1]
          self.points.append(np.matrix(position))
  
      def draw(self, window):
          for point in self.points:
              p = (point - self.parent.camera[0])
              p = np.reshape(p, (4, 1))
              projection = np.dot(self.parent.projection_matrix, p)
              projection = projection.tolist()
              x = projection[0][0]
              y = projection[1][0]
              pygame.draw.circle(window, (255, 255, 255), (int(x) + window.get_width() / 2 - 100, int(y) + window.get_width() / 2- 100) , 1)
  
  </code></pre>
  <p>demo.py: The demo</p>
  <pre><code># Py3D demo
  
  from Py3D.__init__ import *
  import random
  
  win = Py3dWindow(title=&quot;Py3D&quot;)
  win.init_window()
  
  mesh_ = Mesh(win)
  
  side = 1
  
  for i in range(-side, side):
      for j in range(-side, side):
          for k in range(-side, side):
              mesh_.add_point((i, j, k))
  
  win.add_mesh(mesh_)
  
  def move(events):
      for i in events:
          if i.type == 771:
              if i.text == 'w':
                  win.camera[0][2] -= 1
              if i.text == 's':
                  win.camera[0][2] += 1
              if i.text == 'a':
                  win.camera[0][0] -= 1
              if i.text == 'd':
                  win.camera[0][0] += 1
  
  def rotate():
      win.camera[1][0] = 45
      win.camera[1][1] = 45
      win.camera[1][2] = 45
  
  win.on_events(move)
  win.on_update(rotate)
  win.run()
  
  </code></pre>
  <h2>Errors</h2>
  <pre><code>ValueError: shapes (4,4) and (1,4) not aligned: 4 (dim 1) != 1 (dim 0)
  </code></pre>